iPXE
smbios.c
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <stdint.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <assert.h>
30 #include <ipxe/uaccess.h>
31 #include <ipxe/smbios.h>
32 
33 /** @file
34  *
35  * System Management BIOS
36  *
37  */
38 
39 /** SMBIOS entry point descriptor */
40 static struct smbios smbios = {
41  .address = UNULL,
42 };
43 
44 /**
45  * Calculate SMBIOS entry point structure checksum
46  *
47  * @v start Start address of region
48  * @v offset Offset of SMBIOS entry point structure
49  * @v len Length of entry point structure
50  * @ret sum Byte checksum
51  */
52 static uint8_t smbios_checksum ( userptr_t start, size_t offset, size_t len ) {
53  size_t end = ( offset + len );
54  uint8_t sum;
55  uint8_t byte;
56 
57  for ( sum = 0 ; offset < end ; offset++ ) {
58  copy_from_user ( &byte, start, offset, sizeof ( byte ) );
59  sum += byte;
60  }
61  return sum;
62 }
63 
64 /**
65  * Scan for SMBIOS 32-bit entry point structure
66  *
67  * @v start Start address of region to scan
68  * @v len Length of region to scan
69  * @v entry SMBIOS entry point structure to fill in
70  * @ret rc Return status code
71  */
73  struct smbios_entry *entry ) {
74  static size_t offset = 0; /* Avoid repeated attempts to locate SMBIOS */
75  uint8_t sum;
76 
77  /* Try to find SMBIOS */
78  for ( ; ( offset + sizeof ( *entry ) ) <= len ; offset += 0x10 ) {
79 
80  /* Read start of header and verify signature */
81  copy_from_user ( entry, start, offset, sizeof ( *entry ) );
82  if ( entry->signature != SMBIOS_SIGNATURE )
83  continue;
84 
85  /* Verify checksum */
86  if ( ( sum = smbios_checksum ( start, offset,
87  entry->len ) ) != 0 ) {
88  DBG ( "SMBIOS at %08lx has bad checksum %02x\n",
89  user_to_phys ( start, offset ), sum );
90  continue;
91  }
92 
93  /* Fill result structure */
94  DBG ( "Found SMBIOS v%d.%d entry point at %08lx\n",
95  entry->major, entry->minor,
96  user_to_phys ( start, offset ) );
97  return 0;
98  }
99 
100  DBG ( "No SMBIOS found\n" );
101  return -ENODEV;
102 }
103 
104 /**
105  * Scan for SMBIOS 64-bit entry point structure
106  *
107  * @v start Start address of region to scan
108  * @v len Length of region to scan
109  * @v entry SMBIOS entry point structure to fill in
110  * @ret rc Return status code
111  */
113  struct smbios3_entry *entry ) {
114  static size_t offset = 0; /* Avoid repeated attempts to locate SMBIOS */
115  uint8_t sum;
116 
117  /* Try to find SMBIOS */
118  for ( ; ( offset + sizeof ( *entry ) ) <= len ; offset += 0x10 ) {
119 
120  /* Read start of header and verify signature */
121  copy_from_user ( entry, start, offset, sizeof ( *entry ) );
122  if ( entry->signature != SMBIOS3_SIGNATURE )
123  continue;
124 
125  /* Verify checksum */
126  if ( ( sum = smbios_checksum ( start, offset,
127  entry->len ) ) != 0 ) {
128  DBG ( "SMBIOS3 at %08lx has bad checksum %02x\n",
129  user_to_phys ( start, offset ), sum );
130  continue;
131  }
132 
133  /* Fill result structure */
134  DBG ( "Found SMBIOS3 v%d.%d entry point at %08lx\n",
135  entry->major, entry->minor,
136  user_to_phys ( start, offset ) );
137  return 0;
138  }
139 
140  DBG ( "No SMBIOS3 found\n" );
141  return -ENODEV;
142 }
143 
144 /**
145  * Find SMBIOS strings terminator
146  *
147  * @v offset Offset to start of strings
148  * @ret offset Offset to strings terminator, or 0 if not found
149  */
150 static size_t find_strings_terminator ( size_t offset ) {
151  size_t max_offset = ( smbios.len - 2 );
152  uint16_t nulnul;
153 
154  for ( ; offset <= max_offset ; offset++ ) {
155  copy_from_user ( &nulnul, smbios.address, offset, 2 );
156  if ( nulnul == 0 )
157  return ( offset + 1 );
158  }
159  return 0;
160 }
161 
162 /**
163  * Find specific structure type within SMBIOS
164  *
165  * @v type Structure type to search for
166  * @v instance Instance of this type of structure
167  * @v structure SMBIOS structure descriptor to fill in
168  * @ret rc Return status code
169  */
170 int find_smbios_structure ( unsigned int type, unsigned int instance,
171  struct smbios_structure *structure ) {
172  unsigned int count = 0;
173  size_t offset = 0;
174  size_t strings_offset;
175  size_t terminator_offset;
176  int rc;
177 
178  /* Find SMBIOS */
179  if ( ( smbios.address == UNULL ) &&
180  ( ( rc = find_smbios ( &smbios ) ) != 0 ) )
181  return rc;
182  assert ( smbios.address != UNULL );
183 
184  /* Scan through list of structures */
185  while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len ) &&
186  ( ( smbios.count == 0 ) || ( count < smbios.count ) ) ) {
187 
188  /* Read next SMBIOS structure header */
189  copy_from_user ( &structure->header, smbios.address, offset,
190  sizeof ( structure->header ) );
191 
192  /* Determine start and extent of strings block */
193  strings_offset = ( offset + structure->header.len );
194  if ( strings_offset > smbios.len ) {
195  DBG ( "SMBIOS structure at offset %zx with length "
196  "%x extends beyond SMBIOS\n", offset,
197  structure->header.len );
198  return -ENOENT;
199  }
200  terminator_offset = find_strings_terminator ( strings_offset );
201  if ( ! terminator_offset ) {
202  DBG ( "SMBIOS structure at offset %zx has "
203  "unterminated strings section\n", offset );
204  return -ENOENT;
205  }
206  structure->strings_len = ( terminator_offset - strings_offset);
207 
208  DBG ( "SMBIOS structure at offset %zx has type %d, length %x, "
209  "strings length %zx\n", offset, structure->header.type,
210  structure->header.len, structure->strings_len );
211 
212  /* Stop if we have reached an end-of-table marker */
213  if ( ( smbios.count == 0 ) &&
214  ( structure->header.type == SMBIOS_TYPE_END ) )
215  break;
216 
217  /* If this is the structure we want, return */
218  if ( ( structure->header.type == type ) &&
219  ( instance-- == 0 ) ) {
220  structure->offset = offset;
221  return 0;
222  }
223 
224  /* Move to next SMBIOS structure */
225  offset = ( terminator_offset + 1 );
226  count++;
227  }
228 
229  DBG ( "SMBIOS structure type %d not found\n", type );
230  return -ENOENT;
231 }
232 
233 /**
234  * Copy SMBIOS structure
235  *
236  * @v structure SMBIOS structure descriptor
237  * @v data Buffer to hold SMBIOS structure
238  * @v len Length of buffer
239  * @ret rc Return status code
240  */
241 int read_smbios_structure ( struct smbios_structure *structure,
242  void *data, size_t len ) {
243 
244  assert ( smbios.address != UNULL );
245 
246  if ( len > structure->header.len )
247  len = structure->header.len;
248  copy_from_user ( data, smbios.address, structure->offset, len );
249  return 0;
250 }
251 
252 /**
253  * Find indexed string within SMBIOS structure
254  *
255  * @v structure SMBIOS structure descriptor
256  * @v index String index
257  * @v data Buffer for string
258  * @v len Length of string buffer
259  * @ret rc Length of string, or negative error
260  */
261 int read_smbios_string ( struct smbios_structure *structure,
262  unsigned int index, void *data, size_t len ) {
263  size_t strings_start = ( structure->offset + structure->header.len );
264  size_t strings_end = ( strings_start + structure->strings_len );
265  size_t offset;
266  size_t string_len;
267 
268  assert ( smbios.address != UNULL );
269 
270  /* String numbers start at 1 (0 is used to indicate "no string") */
271  if ( ! index )
272  return -ENOENT;
273 
274  for ( offset = strings_start ; offset < strings_end ;
275  offset += ( string_len + 1 ) ) {
276  /* Get string length. This is known safe, since the
277  * smbios_strings struct is constructed so as to
278  * always end on a string boundary.
279  */
280  string_len = strlen_user ( smbios.address, offset );
281  if ( --index == 0 ) {
282  /* Copy string, truncating as necessary. */
283  if ( len > string_len )
284  len = string_len;
286  return string_len;
287  }
288  }
289 
290  DBG ( "SMBIOS string index %d not found\n", index );
291  return -ENOENT;
292 }
293 
294 /**
295  * Get SMBIOS version
296  *
297  * @ret version Version, or negative error
298  */
299 int smbios_version ( void ) {
300  int rc;
301 
302  /* Find SMBIOS */
303  if ( ( smbios.address == UNULL ) &&
304  ( ( rc = find_smbios ( &smbios ) ) != 0 ) )
305  return rc;
306  assert ( smbios.address != UNULL );
307 
308  return smbios.version;
309 }
310 
311 /**
312  * Clear SMBIOS entry point descriptor
313  *
314  */
315 void smbios_clear ( void ) {
316 
317  /* Clear address */
318  smbios.address = UNULL;
319 }
size_t strings_len
Length of strings section.
Definition: smbios.h:135
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
#define SMBIOS_SIGNATURE
Signature for 32-bit SMBIOS entry point.
Definition: smbios.h:35
unsigned short uint16_t
Definition: stdint.h:11
unsigned int count
Number of SMBIOS structures.
Definition: smbios.h:213
Error codes.
#define SMBIOS_TYPE_END
SMBIOS end of table type.
Definition: smbios.h:199
static __always_inline void copy_from_user(void *dest, userptr_t src, off_t src_off, size_t len)
Copy data from user buffer.
Definition: uaccess.h:337
unsigned long user_to_phys(userptr_t userptr, off_t offset)
Convert user pointer to physical address.
int read_smbios_structure(struct smbios_structure *structure, void *data, size_t len)
Copy SMBIOS structure.
Definition: smbios.c:241
size_t len
Length of SMBIOS structures.
Definition: smbios.h:211
#define ENOENT
No such file or directory.
Definition: errno.h:514
SMBIOS entry point descriptor.
Definition: smbios.h:207
struct smbios_header header
Copy of SMBIOS structure header.
Definition: smbios.h:131
#define SMBIOS3_SIGNATURE
Signature for 64-bit SMBIOS entry point.
Definition: smbios.h:39
int find_smbios(struct smbios *smbios)
Access to external ("user") memory.
uint16_t version
SMBIOS version.
Definition: smbios.h:215
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
void smbios_clear(void)
Clear SMBIOS entry point descriptor.
Definition: smbios.c:315
uint32_t start
Starting offset.
Definition: netvsc.h:12
uint8_t len
Length.
Definition: smbios.h:123
int find_smbios_structure(unsigned int type, unsigned int instance, struct smbios_structure *structure)
Find specific structure type within SMBIOS.
Definition: smbios.c:170
Assertions.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
int find_smbios_entry(userptr_t start, size_t len, struct smbios_entry *entry)
Scan for SMBIOS 32-bit entry point structure.
Definition: smbios.c:72
SMBIOS 32-bit entry point.
Definition: smbios.h:49
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
int smbios_version(void)
Get SMBIOS version.
Definition: smbios.c:299
int read_smbios_string(struct smbios_structure *structure, unsigned int index, void *data, size_t len)
Find indexed string within SMBIOS structure.
Definition: smbios.c:261
union aes_table_entry entry[256]
Table entries, indexed by S(N)
Definition: aes.c:26
#define ENODEV
No such device.
Definition: errno.h:509
unsigned char uint8_t
Definition: stdint.h:10
unsigned char byte
Definition: smc9000.h:38
System Management BIOS.
size_t strlen_user(userptr_t userptr, off_t offset)
Find length of NUL-terminated string in user buffer.
uint8_t type
Type.
Definition: smbios.h:121
#define UNULL
Equivalent of NULL for user pointers.
Definition: uaccess.h:36
uint32_t len
Length.
Definition: ena.h:14
uint32_t type
Operating system type.
Definition: ena.h:12
int find_smbios3_entry(userptr_t start, size_t len, struct smbios3_entry *entry)
Scan for SMBIOS 64-bit entry point structure.
Definition: smbios.c:112
userptr_t address
Start of SMBIOS structures.
Definition: smbios.h:209
static uint8_t smbios_checksum(userptr_t start, size_t offset, size_t len)
Calculate SMBIOS entry point structure checksum.
Definition: smbios.c:52
uint16_t count
Number of entries.
Definition: ena.h:22
uint32_t end
Ending offset.
Definition: netvsc.h:18
uint8_t data[48]
Additional event data.
Definition: ena.h:22
uint64_t index
Index of the first segment within the content.
Definition: pccrc.h:21
#define DBG(...)
Print a debugging message.
Definition: compiler.h:498
SMBIOS structure descriptor.
Definition: smbios.h:129
String functions.
static size_t find_strings_terminator(size_t offset)
Find SMBIOS strings terminator.
Definition: smbios.c:150
size_t offset
Offset of structure within SMBIOS.
Definition: smbios.h:133
unsigned long userptr_t
A pointer to a user buffer.
Definition: uaccess.h:33
SMBIOS 64-bit entry point.
Definition: smbios.h:90