SMBIOS Library
SmbiosItem.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 <iomanip>
24 #include <string.h>
25 
26 #include "SmbiosImpl.h"
27 // message.h should be included last.
28 #include "smbios/message.h"
29 
30 using namespace smbiosLowlevel;
31 using namespace std;
32 
33 namespace smbios
34 {
35  ISmbiosItem::~ISmbiosItem()
36  {}
37 
38  ISmbiosItem::ISmbiosItem()
39  {}
40 
41  //
42  // COPY CONSTRUCTOR
43  //
44  SmbiosItem::SmbiosItem (const SmbiosItem & source)
45  : ISmbiosItem(), header (source.header), header_size(source.header_size)
46  {
47  // only one allocation here. If it fails, there is
48  // nothing to rollback, so we are exception safe.
49  // if we add another allocation, we need to restructure this.
50  u8 *newSmbiosItem = new u8[ header_size ];
51  memcpy (newSmbiosItem, source.header, header_size);
52  header = reinterpret_cast<const smbios_structure_header *>(newSmbiosItem);
53 
54  // don't deref header if it isn't valid
55  // the world is not right if this happens.
56  if (!header)
57  {
58  InternalErrorImpl internalError;
59  internalError.setMessageString(_("Not a valid header. header is zero."));
60  throw internalError;
61  }
62 
63  }
64 
65  //
66  // REGULAR CONSTRUCTOR
67  //
69  : ISmbiosItem(), header(init_header), header_size(0)
70  {
71  // we need to copy all of our data out of the SmbiosTable
72  // so that we have our own copy. This effectively lets
73  // smbiosItem have a separate lifetime from it's containing
74  // table.
75  //
76  // we do all of the stuff below, up to the "new", to figure
77  // out the size of the item.
78 
79  // don't deref header if it isn't valid
80  // the world is not right if this happens.
81  if (!header)
82  {
83  InternalErrorImpl internalError;
84  internalError.setMessageString(_("Not a valid header. header is zero."));
85  throw internalError;
86  }
87 
88  // hop over to the next struct using smbios parsing rules
89  // see smbiostable code for more details
90  const u8 *nextStruct = reinterpret_cast<const u8 *>(header) + header->length ;
91 
92  // skip over the strings in the string table. It ends with a double null
93  while (*nextStruct || nextStruct[1])
94  nextStruct++;
95 
96  // skip over the actual double null
97  nextStruct += 2;
98 
99  // now we are at the next struct, we know the size
100  // of the struct we are supposed to be pointing at.
101  header_size = nextStruct - reinterpret_cast<const u8 *>(header);
102 
103  // only one allocation here. If it fails, there is
104  // nothing to rollback, so we are safe.
105  // if we add another allocation, we need to restructure this.
106  u8 *newSmbiosItem = new u8[header_size];
107  memcpy (newSmbiosItem, header, header_size);
108  header = reinterpret_cast<const smbios_structure_header *>(newSmbiosItem);
109  }
110 
112  {
113  // its ugly because microsoft vc++ 6 sucks so much.
114  delete [] const_cast<u8 *>(reinterpret_cast<const u8 *>(header));
115  header = 0;
116  }
117 
118  // gcc workaround. overprotective git. #!#$J@*(&$%
119  // This is only used to format informational output stuff.
120  // it loses precision, so don't do it except for display
121  static u32 force_u64_to_u32(u64 orig)
122  {
123  // only gives correct results for little endian (I think)
124  // but shouldn't matter, as it is for information purposes only.
125  //
126  // use union to fix gcc type-punned pointer warning
127  union
128  {
129  u64 orig;
130  u32 recast;
131  } temp;
132 
133  temp.orig = orig;
134  return temp.recast;
135  }
136 
137  // Lifetime of returned char* is the same as the SmbiosItem
138  // FIXME: This function needs to make sure it doesn't run past the end of the table for
139  // malformed strings.
140  //
141  // Invariant: will always return a valid const char * or throw an exception
142  const char *SmbiosItem::getStringByStringNumber (u8 which) const
143  {
144  const char *string_pointer = reinterpret_cast<const char *>(header);
145 
146  // either user is an idiot and should go read the spec,
147  // _or_ there is a '\0' in the table, indicating
148  // the string does not exist. :-(
149  // In either case, we throw an exception.
150  if (!which) //strings are numbered beginning with 1
151  {
152  throw StringUnavailableImpl(_("String does not exist."));
153  }
154 
155  // start out at the end of the header. This is where
156  // the first string starts
157  string_pointer += header->length;
158 
159  for (; which > 1; which--)
160  {
161  string_pointer += strlen (string_pointer);
162  string_pointer++; // skip past '\0'
163 
164  // check that we don't overflow this item
165  // additionally, split test into temp vars outside if() to work
166  // around astyle formatting bug where it will break code.
167  const u8 *cur_loc = reinterpret_cast<const u8 *>(string_pointer);
168  const u8 *base_loc = reinterpret_cast<const u8 *>(header);
169  if( cur_loc >= base_loc + header_size)
170  {
171  ParseExceptionImpl parseException;
172  parseException.setMessageString(_("Overflow while getting byte data at location: cur_loc >= base_loc + header_size\n cur_loc : %(cur_loc)i\n base_loc : %(base_loc)i\n header_size : %(header_size)i "));
173  parseException.setParameter("cur_loc", force_u64_to_u32(reinterpret_cast<u64>(cur_loc)));
174  parseException.setParameter("base_loc", force_u64_to_u32(reinterpret_cast<u64>(base_loc)));
175  parseException.setParameter("header_size",static_cast<u32>(header_size));
176  throw parseException;
177  }
178 
179  // if it is still '\0', that means we are
180  // at the end of this item and should stop.
181  // user gave us a bad index
182  if( ! *string_pointer )
183  {
184  throw StringUnavailableImpl(_("The string does not exist. Bad index caused this error"));
185  }
186  }
187 
188  return string_pointer;
189  }
190 
191  std::auto_ptr<ISmbiosItem> SmbiosItem::clone()
192  {
193  return auto_ptr<ISmbiosItem>(new SmbiosItem (*this));
194  }
195 
196  std::auto_ptr<const ISmbiosItem> SmbiosItem::clone() const
197  {
198  return auto_ptr<const ISmbiosItem>(new SmbiosItem (*this));
199  }
200 
202  {
203  return header->type;
204  }
205 
207  {
208  return header->length;
209  }
210 
212  {
213  return header->handle;
214  }
215 
216  void checkItemBounds( size_t total_size, size_t length, size_t offset, size_t size)
217  {
218  DataOutOfBoundsImpl dataOutOfBounds;
219  dataOutOfBounds.setParameter("offset",static_cast<int>(offset));
220  dataOutOfBounds.setParameter("header_length",static_cast<int>(total_size));
221 
222  // tricky. Need all three tests here in this order to avoid security hole
223  if( offset > length )
224  {
225  dataOutOfBounds.setMessageString(_("Attempt to access data outside the length of header. offset : %(offset)i, header_length : %(header_length)i"));
226  throw dataOutOfBounds;
227  }
228 
229  if( offset + size < offset )
230  {
231  dataOutOfBounds.setMessageString(_("Attempt to access data outside the length of header. offset : %(offset)i, header_length : %(header_length)i"));
232  throw dataOutOfBounds;
233  }
234 
235  if( offset + size > length )
236  {
237  dataOutOfBounds.setMessageString(_("Attempt to access data outside the length of header. offset : %(offset)i, header_length : %(header_length)i"));
238  throw dataOutOfBounds;
239  }
240 
241  if( offset >= total_size ) // world gone mad check.
242  // data is inside what the header says is
243  // the length, but outside the range of the
244  // buffer we are using to hold the header.
245  // Impossible?
246  {
247  dataOutOfBounds.setMessageString(_("Attempt to access data outside header buffer. Impossible situation! offset : %(offset)i, header_length : %(header_length)i"));
248  throw dataOutOfBounds;
249  }
250 
251  }
252 
253  void SmbiosItem::getData(unsigned int offset, u8 *out, size_t size ) const
254  {
255  checkItemBounds( header_size, header->length, offset, size );
256  memcpy(out, reinterpret_cast<const u8 *>(header)+offset, size);
257  }
258 
259  const u8 *SmbiosItem::getBufferCopy(size_t &size) const
260  {
261  size = header_size;
262 
263  const u8 *newBuffer = new u8[ size ];
264  memcpy (const_cast<u8 *>(newBuffer), header, size);
265  return newBuffer;
266  }
267 
269  {
270  return header_size;
271  }
272 
273  void SmbiosItem::fixup( const SmbiosWorkaroundTable *workaround ) const
274  {
275  u8 *buffer = const_cast<u8 *>(reinterpret_cast<const u8 *>(header));
276  workaround->fixupItem( this, buffer, header_size );
277  }
278 
279  ostream & SmbiosItem::streamify (ostream & cout) const
280  {
281  if (header == 0) // violates class invariant, should never happen
282  cout << "operator << on an uninitialized SmbiosItem!";
283  else
284  {
285  std::ios::fmtflags old_opts = cout.flags ();
286  cout << "Handle 0x" << hex << setfill ('0') <<
287  setw (4) << getHandle () << endl;
288  cout << "\tDMI type 0x" << static_cast<int>(getType()) << dec <<
289  ", " << static_cast<int>(getLength()) << " bytes." <<
290  endl;
291  cout.flags (old_opts);
292  }
293  return cout;
294  }
295 
296 
297  /**************************************************************************
298  * OUT OF LINE HELPERS
299  *************************************************************************/
300 
302  {
303  return getU8_FromItem(item, 0);
304  }
305 
307  {
308  return getU8_FromItem(item, 1);
309  }
310 
312  {
313  return getU16_FromItem(item, 2);
314  }
315 
316  u8 getU8_FromItem(const ISmbiosItem &item, unsigned int offset)
317  {
318  u8 retval = 0;
319  item.getData(offset, reinterpret_cast<u8 *>(&retval), sizeof(retval));
320  return retval;
321  }
322 
323  u16 getU16_FromItem(const ISmbiosItem &item, unsigned int offset)
324  {
325  u16 retval = 0;
326  item.getData(offset, reinterpret_cast<u8 *>(&retval), sizeof(retval));
327  return retval;
328  }
329 
330  u32 getU32_FromItem(const ISmbiosItem &item, unsigned int offset)
331  {
332  u32 retval = 0;
333  item.getData(offset, reinterpret_cast<u8 *>(&retval), sizeof(retval));
334  return retval;
335  }
336 
337  u64 getU64_FromItem(const ISmbiosItem &item, unsigned int offset)
338  {
339  u64 retval = 0;
340  item.getData(offset, reinterpret_cast<u8 *>(&retval), sizeof(retval));
341  return retval;
342  }
343 
344  const char *getString_FromItem(const ISmbiosItem &item, unsigned int offset)
345  {
346  u8 stringNum = 0;
347  getData(item, offset, stringNum);
348  return item.getStringByStringNumber(stringNum);
349  }
350 
351  void *getBits_FromItem ( const ISmbiosItem &item, unsigned int offset, void *out, unsigned int lsb, unsigned int msb )
352  {
353  u64 bitfield = 0;
354 
355  //If msb is less/equal to lsb, they are only requesting a single bit
356  if(msb <= lsb)
357  msb=lsb;
358 
359  if(msb > 63)
360  {
361  DataOutOfBoundsImpl dataOutOfBounds;
362  dataOutOfBounds.setParameter("lsb",lsb);
363  dataOutOfBounds.setParameter("msb",msb);
364  dataOutOfBounds.setMessageString(_("The total length of bit field is out of bounds. The largest accessible bit is 63. lsb: %(lsb)i , msb: %(msb)i"));
365  throw dataOutOfBounds;
366  }
367 
368  // calculate length of bit field based on msb/lsb
369  unsigned int fieldLen = ((msb+1)/8) + (((msb+1)%8)?1:0);
370 
371  // request data from item
372  item.getData(offset, reinterpret_cast<u8 *>(&bitfield), fieldLen);
373 
374  // mask off everything but requested bits and shift down
375  unsigned int bitlen = (msb-lsb) + 1;
376  bitfield = (bitfield >> lsb) & ((1<<bitlen)-1);
377 
378  if(out)
379  memcpy(out, &bitfield, ((bitlen)/8) + (((bitlen)%8)?1:0));
380 
381  return out;
382  }
383 
384  bool isBitSet(const ISmbiosItem *itemPtr, unsigned int offset, unsigned int bitToTest)
385  {
386  bool retval = false;
387 
388  unsigned int byte = bitToTest / 8;
389  u8 fieldValue = getU8_FromItem(*itemPtr, offset + byte );
390  if (fieldValue & (1 << (bitToTest%8)))
391  retval = true;
392 
393  return retval;
394  }
395 
396  ostream & operator << (ostream & cout, const ISmbiosItem & item)
397  {
398  return item.streamify (cout);
399  }
400 }