SMBIOS Library
RbuHdr.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 #include "smbios/compat.h"
21 
22 #include <errno.h>
23 #include <string.h> // strerror()
24 #include <iostream>
25 #include <sstream>
26 #include <iomanip>
27 
28 #include "RbuImpl.h"
29 #include "smbios/IToken.h"
30 #include "smbios/SystemInfo.h"
31 
32 // always include last if included.
33 #include "smbios/message.h" // not needed outside of this lib. (mainly for gettext i18n)
34 
35 using namespace std;
36 
37 namespace rbu
38 {
39 
40  RbuFactory::~RbuFactory() throw() {}
41  RbuFactory::RbuFactory() {}
42 
43  RbuFactory *RbuFactory::getFactory()
44  {
45  // reinterpret_cast<...>(0) to ensure template parameter is correct
46  // this is a workaround for VC6 which cannot use explicit member template
47  // funciton initialization.
48  return RbuFactoryImpl::getFactory(reinterpret_cast<RbuFactoryImpl *>(0));
49  }
50 
51  RbuFactoryImpl::~RbuFactoryImpl() throw() { }
52  RbuFactoryImpl::RbuFactoryImpl() { }
53  IRbuHdr *RbuFactoryImpl::makeNew(std::string filename)
54  {
55  return new RbuHdr( filename );
56  }
57 
58  IRbuHdr::IRbuHdr() {}
59  IRbuHdr::~IRbuHdr() {}
60 
61 
62  RbuHdr::RbuHdr( string filename ) : hdrFh(fopen(filename.c_str(), "rb"))
63  {
64  if(!hdrFh)
65  {
66  string errmsg = strerror(errno);
67  throw HdrFileIOErrorImpl(errmsg);
68  }
69 
70  memset(&header, 0, sizeof(header));
71  size_t bytesRead = fread(&header, 1, sizeof(header), hdrFh); // short read handled
72  if (bytesRead != sizeof(header))
73  {
74  fclose(hdrFh);
75  hdrFh=0;
76  throw InvalidHdrFileImpl("Couldnt read full header.");
77  }
78  fseek(hdrFh, 0, 0);
79  if (
80  header.headerId[0] == '$' &&
81  header.headerId[1] == 'R' &&
82  header.headerId[2] == 'B' &&
83  header.headerId[3] == 'U'
84  )
85  {
86  }
87  else
88  {
89  fclose(hdrFh);
90  hdrFh=0;
91  throw InvalidHdrFileImpl("Did not pass header $RBU check.");
92  }
93 
94  // initialize system id list
95  memset(sysIdList, 0, sizeof(sysIdList));
96  for( unsigned int i=0; i < (sizeof(header.systemIdList)/sizeof(header.systemIdList[0])); ++i)
97  {
98  if(i >= static_cast<unsigned int>(NUM_SYS_ID_IN_HDR)) break; // safety...
99 
100  // bits 8:10 are hw rev id, extract them and paste the rest together
101  u32 val = header.systemIdList[i];
102  u32 id = (val & 0xFF) | ((val & 0xF800) >> 3);
103 
104  if(!id) break;
105 
106  sysIdList[i] = id;
107  }
108  }
109 
111  {
112  if(hdrFh)
113  fclose(hdrFh);
114  }
115 
116  string RbuHdr::getBiosVersion() const
117  {
118  string ver("");
119  if( header.headerMajorVer < 2 )
120  {
121  if( isalnum(header.biosVersion[0]) )
122  ver = ver + header.biosVersion[0];
123 
124  if( isalnum(header.biosVersion[1]) )
125  ver = ver + header.biosVersion[1];
126 
127  if( isalnum(header.biosVersion[2]) )
128  ver = ver + header.biosVersion[2];
129  }
130  else
131  {
132  std::ostringstream rep;
133  rep << static_cast<int>(header.biosVersion[0]) << "."
134  << static_cast<int>(header.biosVersion[1]) << "."
135  << static_cast<int>(header.biosVersion[2]);
136  ver = rep.str();
137  }
138  return ver;
139  }
140 
141  void RbuHdr::getHdrVersion(unsigned int &major, unsigned int &minor) const
142  {
143  major = header.headerMajorVer;
144  minor = header.headerMinorVer;
145  }
146 
147  const u32 * RbuHdr::getSystemIdList() const {return sysIdList;}
148 
149 
150  void RbuHdr::doUpdate() const {}
151 
152  bool checkSystemId(const IRbuHdr &hdr, u16 sysId )
153  {
154  const u32 *idList = hdr.getSystemIdList();
155  for (const u32 *ptr = idList; *ptr; ++ptr)
156  if( *ptr == sysId )
157  return true;
158 
159  return false;
160  }
161 
162  FILE *RbuHdr::getFh() const
163  {
164  return hdrFh;
165  }
166 
167  ostream & operator << (ostream & cout, const IRbuHdr & hdr)
168  {
169  return hdr.streamify (cout);
170  }
171 
172  ostream & RbuHdr::streamify( ostream &cout ) const
173  {
174  std::ios::fmtflags old_opts = cout.flags ();
175  cout << "HeaderId : "
176  << header.headerId[0]
177  << header.headerId[1]
178  << header.headerId[2]
179  << header.headerId[3] << endl;
180 
181  cout << "Header Length: " << static_cast<int>(header.headerLength) << endl;
182  cout << "Header Major Ver: " << static_cast<int>(header.headerMajorVer) << endl;
183  cout << "Header Minor Ver: " << static_cast<int>(header.headerMinorVer) << endl;
184  cout << "Num Systems: " << static_cast<int>(header.numSystems) << endl;
185 
186  cout << "Version: " << getBiosVersion() << endl;
187 
188  char quickCheck[41] = {0};
189  strncpy(quickCheck, header.quickCheck, 40);
190  cout << "Quick Check: " << quickCheck << endl;
191 
192  cout << "System ID List:" << hex;
193 
194  for (const u32 *ptr = sysIdList; *ptr; ++ptr)
195  cout << " 0x" << setfill ('0') << setw (4) << *ptr;
196 
197  cout << endl << dec;
198  cout.flags (old_opts);
199 
200  return cout;
201  }
202 
203  // Helper functions
204  static string stringToLower(string in)
205  {
206  for(unsigned int i=0;i<in.length();i++)
207  in[i] = tolower(in[i]);
208 
209  return in;
210  }
211 
212  static int compareSamePrefixOldBiosVersion(std::string ver1, std::string ver2)
213  {
214  if(ver1 > ver2)
215  return -1;
216 
217  if(ver1 < ver2)
218  return 1;
219 
220  // should _NEVER_ get here.
221  return 0;
222  }
223 
224  static int compareOldBiosVersion(std::string ver1, std::string ver2)
225  {
226  if (ver1[0] == ver2[0])
227  return compareSamePrefixOldBiosVersion(ver1, ver2);
228 
229  if (tolower(ver1[0]) == 'a')
230  return -1;
231 
232  if (tolower(ver2[0]) == 'a')
233  return 1;
234 
235  if (tolower(ver1[0]) == 'x')
236  return -1;
237 
238  if (tolower(ver2[0]) == 'x')
239  return 1;
240 
241  if (tolower(ver1[0]) == 'p')
242  return -1;
243 
244  if (tolower(ver2[0]) == 'p')
245  return 1;
246 
247  if (ver1[0] > ver2[0])
248  return -1;
249 
250  return 1;
251  }
252 
253  // should be static, but we want to test this in unit test suite. NOT PART OF PUBLIC API
254  void splitNewVersion(std::string ver, unsigned int &maj, unsigned int &min, unsigned int &ext)
255  {
256  unsigned int verSplit[3] = {0,};
257 
258  DCOUT("splitNewVersion( \""<< ver <<"\" )" << endl);
259 
260  size_t start=0, end=0;
261  for(int i=0; i<3; i++)
262  {
263  string verPart = "";
264  end = ver.find('.', start);
265  verPart.append(ver, start, end - start);
266 
267  verSplit[i] = strtoul(verPart.c_str(), 0, 10);
268  DCOUT("Start(" << start << ") End(" << end << ") verPart(\"" << verPart << "\") verSplit[" << i << "](" << verSplit[i] << ")" << endl);
269 
270  if( end == string::npos )
271  break;
272 
273  start = end + 1;
274  }
275 
276  maj = verSplit[0];
277  min = verSplit[1];
278  ext = verSplit[2];
279 
280  DCOUT("Return: (" << maj << ", " << min << ", " << ext << ")" << endl);
281  }
282 
283 
284  static int compareNewBiosVersion(std::string ver1, std::string ver2)
285  {
286  unsigned int maj1, min1, ext1;
287  unsigned int maj2, min2, ext2;
288  splitNewVersion( ver1, maj1, min1, ext1 );
289  splitNewVersion( ver2, maj2, min2, ext2 );
290 
291  // check for 90-99 major. Should never win against non-90-99 ver:
292  const unsigned int SPECIAL_VER_START = 90;
293  if (maj1 >= SPECIAL_VER_START && maj2 < SPECIAL_VER_START)
294  return 1;
295  if (maj1 < SPECIAL_VER_START && maj2 >= SPECIAL_VER_START)
296  return -1;
297 
298  if (maj1 > maj2)
299  return -1;
300  if (maj1 < maj2)
301  return 1;
302 
303  if (min1 > min2)
304  return -1;
305  if (min1 < min2)
306  return 1;
307 
308  if (ext1 > ext2)
309  return -1;
310  if (ext1 < ext2)
311  return 1;
312 
313  // should never get here as == versions should already be handled.
314  return 0;
315  }
316 
317 
318  int compareBiosVersion(std::string ver1, std::string ver2)
319  {
320  ver1 = stringToLower(ver1);
321  ver2 = stringToLower(ver2);
322 
323  // same string
324  if(ver1 == ver2)
325  return 0;
326 
327  // both old style
328  if ( isalpha(ver1[0]) && isalpha(ver2[0]) )
329  return compareOldBiosVersion(ver1, ver2);
330 
331  // one new, one old, new wins
332  if ( ! isalpha(ver1[0]) && isalpha(ver2[0]) )
333  return -1;
334 
335  // one new, one old, new wins
336  if ( isalpha(ver1[0]) && !isalpha(ver2[0]) )
337  return 1;
338 
339  if ( ! isalpha(ver1[0]) && !isalpha(ver2[0]) )
340  return compareNewBiosVersion(ver1, ver2);
341 
342  // cannot get here...
343  return 0;
344  }
345 
346 
347  // private functions
348 
350  {
351  packet_type pt = pt_mono;
352  try
353  {
355  const smbios::ISmbiosTable *table = smbiosFactory->getSingleton();
356  const smbios::ISmbiosItem &rbuStructure = *((*table)[RBU_SMBIOS_STRUCT]);
357 
358  u8 byte = getU8_FromItem(rbuStructure, 0x0F); // Characteristics field
359  if( byte & 0x01 )
360  pt = pt_packet;
361  }
362  catch(const smbios::DataOutOfBounds &)
363  {
364  // only happens when old-style struct present w/o characteristics field
365  // this means only mono supported.
366  }
367  return pt;
368  }
369 
371  {
373  smbios::ITokenTable *tokenTable = ttFactory->getSingleton();
374  (*tokenTable)[ RBU_ACTIVATE ]->activate();
375  }
376 
378  {
380  smbios::ITokenTable *tokenTable = ttFactory->getSingleton();
381  (*tokenTable)[ RBU_CANCEL ]->activate();
382  }
383 
384  void checksumPacket(rbu_packet *pkt, size_t size)
385  {
386  u16 *buf = reinterpret_cast<u16 *>(pkt);
387  pkt->pktChksum = 0;
388 
389  u16 csum = 0;
390  for(size_t i=0; i<size/2; ++i)
391  csum = csum + buf[i];
392 
393  pkt->pktChksum = -csum;
394  }
395 
396  void createPacket(char *buffer, size_t bufSize, size_t imageSize)
397  {
398  // set up packet
399  rbu_packet *pkt = reinterpret_cast<rbu_packet *>(buffer);
400 
401  pkt->pktId = 0x4B505224; //2452504B; // must be '$RPK'
402  pkt->pktSize = bufSize / 1024; // size of packet in KB
403  pkt->reserved1 = 0; //
404  pkt->hdrSize = 2; // size of packet header in paragraphs (16 byte chunks)
405  int datasize = bufSize - (16 * pkt->hdrSize);
406 
407  pkt->reserved2 = 0; //
408  pkt->pktSetId = 0x12345678; // unique id for packet set, can be anything
409  pkt->pktNum = 0; // sequential pkt number (only thing that changes)
410  pkt->totPkts = (imageSize/datasize) + ((imageSize % datasize) ? 1:0) + 1;// total number of packets
411  pkt->pktVer = 1; // version == 1 for now
412  pkt->pktChksum = 0; // sum all bytes in pkt must be zero
413 
414  checksumPacket(pkt, bufSize);
415  }
416 }