SMBIOS Library
SmbiosStrategy.cpp
Go to the documentation of this file.
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=c:cindent:textwidth=0:
3  *
4  * Copyright (C) 2005 Dell Inc.
5  * by Michael Brown <Michael_E_Brown@dell.com>
6  * Licensed under the Open Software License version 2.1
7  *
8  * Alternatively, you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published
10  * by the Free Software Foundation; either version 2 of the License,
11  * or (at your option) any later version.
12 
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU General Public License for more details.
17  */
18 
19 // compat header should always be first header if including system headers
20 #define LIBSMBIOS_SOURCE
21 #include "smbios/compat.h"
22 
23 #include <sstream>
24 #include <string.h>
25 
26 #include "smbios/IMemory.h"
27 #include "SmbiosImpl.h"
28 
29 // message.h should be included last.
30 #include "smbios/message.h"
31 
32 using namespace smbiosLowlevel;
33 using namespace std;
34 
35 #if defined(DEBUG_SMBIOS_STRATEGY)
36 # define DCOUT(line) do { cout << line; } while(0)
37 # define DCERR(line) do { cerr << line; } while(0)
38 #else
39 # define DCOUT(line) do {} while(0)
40 # define DCERR(line) do {} while(0)
41 #endif
42 
43 namespace smbios
44 {
45 
46  // validate the smbios table entry point
49  bool strict,
50  ParseExceptionImpl &parseException
51  )
52  {
53  // This code checks for the following:
54  // entry point structure checksum : As per the specs
55  // anchor string : As per the specs
56  //
57  bool retval = true;
58 
59  u8 checksum = 0;
60  const u8 *ptr = reinterpret_cast<const u8*>(tempTEP);
61  // don't overrun tempTEP if BIOS is buggy... (note sizeof() test here)
62  // added especially to deal with buggy Intel BIOS.
63  for( unsigned int i = 0; i < sizeof(*tempTEP); ++i )
64  {
65  // stupid stuff to avoid MVC++ .NET runtime exception check for cast to different size
66  checksum = (checksum + ptr[i]) & 0xFF;
67  }
68 
69  ostringstream oss;
70 
71  DCERR("_DMI_ anchor: " << tempTEP->anchor[0] << tempTEP->anchor[1] << tempTEP->anchor[2] << tempTEP->anchor[3] << tempTEP->anchor[4] << tempTEP->anchor[5] << endl);
72  if(memcmp(tempTEP->anchor,"_DMI_",5)!=0) // Checking intermediate anchor string
73  {
74  oss << _("Intermediate anchor string does not match. anchor string: %(dmi_anchor)s") << endl;
75  retval = false; // validation failed
76  }
77 
78  DCERR("_DMI_ checksum: " << (int)checksum << endl);
79  if(checksum) // Checking entry point structure checksum
80  {
81  oss << _("Checksum check for table entry point should be zero. checksum: %(dmi_checksum)i ") << endl;
82  retval = false; // validation failed
83  }
84 
85  parseException.setParameter("dmi_anchor", reinterpret_cast<const char *>(tempTEP->anchor));
86  parseException.setParameter("dmi_checksum", static_cast<int>(checksum));
87 
88  return retval;
89  }
90 
91 
92 
93 
94  // validate the smbios table entry point
97  bool strict,
98  ParseExceptionImpl &parseException
99  )
100  {
101  // This code checks for the following:
102  // entry point structure checksum : As per the specs
103  // smbios major version : As per the specs
104  // Intermediate anchor string : As per the specs
105  //
106  // This code does not check the following:
107  // intermediate checksum: the main checksum covers the
108  // entire area
109  // and should be sufficient, plus there is a
110  // possibility for
111  // BIOS bugs in this area.
112  //
113  // minor version: according to the spec, this parser should
114  // work
115  // with any change in minor version. The spec says this
116  // parser
117  // will break if major version changes, so we check
118  // that.
119  //
120 
121  bool retval = true;
122 
123  u8 checksum = 0;
124  const u8 *ptr = reinterpret_cast<const u8*>(tempTEP);
125  // don't overrun tempTEP if BIOS is buggy... (note sizeof() test here)
126  // added especially to deal with buggy Intel BIOS.
127  for( unsigned int i = 0; (i < static_cast<unsigned int>(tempTEP->eps_length)) && (i < sizeof(*tempTEP)); ++i )
128  {
129  // stupid stuff to avoid MVC++ .NET runtime exception check for cast to different size
130  checksum = (checksum + ptr[i]) & 0xFF;
131  }
132 
133  ostringstream oss;
134  oss << _("validation of table entry point failed") << endl;
135 
136  validateDMITableEntryPoint( &(tempTEP->dmi), strict, parseException );
137 
138  DCERR("strict table checking: " << strict << endl);
139 
140 
141  DCERR("_SM_ checksum: " << (int)checksum << endl);
142 
143  if(checksum) // Checking entry point structure checksum
144  {
145  oss << _("Checksum check for table entry point should be zero. checksum: %(checksum)i ") << endl;
146  retval = false; // validation failed
147  }
148 
149  DCERR("major_ver: " << (int)tempTEP->major_ver << endl);
150  if(tempTEP->major_ver!=0x02) // Checking smbios major version
151  {
152  oss << _("Major version of table entry point should be 2: %(major_version)i") << endl;
153  retval = false; // validation failed
154  }
155 
156  // Entry Point Length field is at least 0x1f.
157  DCERR("eps_length: " << (int)tempTEP->eps_length << endl);
158  if(tempTEP->eps_length < 0x0f)
159  {
160  oss << _("Entry Point Length field is at least 0x1f : %(eps_length)i") << endl;
161  retval = false; // validation failed
162  }
163 
164  parseException.setParameter("checksum", static_cast<int>(checksum));
165  parseException.setParameter("major_version", static_cast<int>(tempTEP->major_ver));
166  parseException.setParameter("eps_length", static_cast<int>(tempTEP->eps_length));
167  parseException.setMessageString(oss.str());
168 
169  return retval;
170  }
171 
172 
173 
174 
175 
176  bool SmbiosMemoryStrategy::getSmbiosTable(const u8 **smbiosBuffer, smbiosLowlevel::smbios_table_entry_point *table_header, bool strict)
177  {
178  bool ret = false;
179  try
180  {
181  // allocates no mem
182  DCERR("trying SmbiosMemoryStrategy" << endl);
183  getSmbiosTableHeader(table_header, strict);
184 
185  // allocates mem, but frees on exception
186  getSmbiosTableBuf(smbiosBuffer, *table_header);
187  if(smbiosBuffer)
188  ret = true;
189  }
190  catch(const exception &e)
191  {
192  UNREFERENCED_PARAMETER(e); // avoid unused var warning when !DEBUG
193  DCERR("got Exception: " << e.what() << endl);
194  }
195 
196  DCERR(" ret for SmbiosMemoryStrategy is: " << ret << endl);
197  return ret;
198  }
199 
200  void SmbiosMemoryStrategy::getSmbiosTableBuf(const u8 **smbiosBuffer, smbiosLowlevel::smbios_table_entry_point table_header)
201  {
203 
204  // new throws exception, no need to test.
205  u8 *newSmbiosBuffer = new u8[table_header.dmi.table_length];
206  try
207  {
208  mem->fillBuffer( newSmbiosBuffer, table_header.dmi.table_address, table_header.dmi.table_length );
209 
210  //delete old one, if necessary
211  if( 0 != *smbiosBuffer )
212  {
213  memset (const_cast<u8 *>(*smbiosBuffer), 0, sizeof (**smbiosBuffer));
214  delete [] const_cast<u8 *>(*smbiosBuffer);
215  *smbiosBuffer = 0;
216  }
217  }
218  catch(...)
219  {
220  delete [] newSmbiosBuffer;
221  newSmbiosBuffer = 0;
222  throw;
223  }
224 
225  *smbiosBuffer = reinterpret_cast<const u8 *>(newSmbiosBuffer);
226  }
227 
228  // allocates no memory, constructs no objects.
229  // can raise an exception
230  void SmbiosMemoryStrategy::getSmbiosTableHeader(smbiosLowlevel::smbios_table_entry_point *table_header, bool strict)
231  {
233 
234  unsigned long fp = E_BLOCK_START;
235  if( offset )
236  fp = offset;
237 
238  ParseExceptionImpl parseException;
239  if( offset )
240  {
241  DCERR("SmbiosMemoryStrategy::getSmbiosTableHeader() using hardcoded offset: " << hex << offset << endl);
242  parseException.setMessageString(_("SMBIOS Header not found at offset: %(offsetValue)i"));
243  parseException.setParameter("offsetValue",offset);
244  }
245  else
246  {
247  DCERR("SmbiosMemoryStrategy::getSmbiosTableHeader() Memory scan for smbios table." << endl);
248  parseException.setMessageString(_("SMBIOS Header not found in search."));
249  }
250 
251  // tell the memory subsystem that it can optimize here and
252  // keep memory open while we scan rather than open/close/open/close/...
253  // for each fillBuffer() call
254  //
255  // this would be safer if we used spiffy c++ raii technique here
256  mem->decReopenHint();
257 
258  smbios_table_entry_point tempTEP;
259  memset(&tempTEP, 0, sizeof(tempTEP));
260  while ( (fp + sizeof(tempTEP)) < F_BLOCK_END)
261  {
262  mem->fillBuffer(
263  reinterpret_cast<u8 *>(&tempTEP),
264  fp,
265  sizeof(tempTEP)
266  );
267 
268  // search for promising looking headers
269  // first, look for old-style DMI header
270  if (memcmp (&tempTEP, "_DMI_", 5) == 0)
271  {
272  DCERR("Found _DMI_ anchor. Trying to parse legacy DMI structure." << endl);
273  dmi_table_entry_point *dmiTEP = reinterpret_cast<dmi_table_entry_point *>(&tempTEP);
274  memmove(&(tempTEP.dmi), &dmiTEP, sizeof(dmi_table_entry_point));
275  // fake the rest of the smbios table entry point...
276  tempTEP.major_ver=2;
277  tempTEP.minor_ver=0;
278  if(validateDMITableEntryPoint(dmiTEP, strict, parseException))
279  {
280  DCERR("Found valid _DMI_ entry point at offset: " << fp << endl);
281  break;
282  }
283  }
284 
285  // then, look for new-style smbios header. This will always
286  // occur before _DMI_ in memory
287  if (offset || (memcmp (&tempTEP, "_SM_", 4) == 0))
288  {
289  // if we are passed a hardcoded offset (EFI?), it is possible
290  // that machine doesnt have _SM_ anchor, but still has valid
291  // table. Just try validating the table and skip _SM_ check.
292  DCERR("Found _SM_ anchor or using hardcoded offset. Trying to parse Smbios Entry Point." << endl);
293  if(validateSmbiosTableEntryPoint(&tempTEP, strict, parseException))
294  {
295  DCERR("Found valid _SM_ entry point at offset: " << fp << endl);
296  break;
297  }
298  }
299 
300  // previous if() would have broken out if we have a valid
301  // table header. if offset is set, then we are not supposed
302  // to be scanning through memory. We didn't find a table,
303  // so there is nothing to do but raise an exception.
304  if (offset)
305  {
306  // dont need memory optimization anymore
307  mem->incReopenHint();
308  throw parseException; // previously set up.
309  }
310 
311  fp += 16;
312  }
313 
314  // dont need memory optimization anymore
315  mem->incReopenHint();
316 
317  // bad stuff happened if we got to here and fp > 0xFFFFFL
318  if ((fp + sizeof(tempTEP)) >= F_BLOCK_END)
319  throw parseException; // previously set up.
320 
321  // found it. set offset for future reference (no need to search.)
322  offset = fp;
323  memcpy( const_cast<smbios_table_entry_point *>(table_header), &tempTEP, sizeof(*table_header) );
324  }
325 }