1 #include "impl.hpp"
2
3 #include "vpdecc/vpdecc.h"
4
5 #include "const.hpp"
6 #include "defines.hpp"
7 #include "ibm_vpd_utils.hpp"
8 #include "types.hpp"
9 #include "vpd_exceptions.hpp"
10
11 #include <algorithm>
12 #include <exception>
13 #include <iomanip>
14 #include <iostream>
15 #include <iterator>
16 #include <sstream>
17 #include <tuple>
18 #include <unordered_map>
19
20 namespace openpower
21 {
22 namespace vpd
23 {
24 namespace parser
25 {
26 using namespace openpower::vpd::constants;
27 using namespace openpower::vpd::exceptions;
28
29 static const std::unordered_map<std::string, Record> supportedRecords = {
30 {"VINI", Record::VINI}, {"OPFR", Record::OPFR}, {"OSYS", Record::OSYS}};
31
32 static const std::unordered_map<std::string, internal::KeywordInfo>
33 supportedKeywords = {
34 {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)},
35 {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)},
36 {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)},
37 {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)},
38 {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)},
39 {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)},
40 {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)},
41 {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::MB)},
42 {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)},
43 {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)},
44 {"VP", std::make_tuple(record::Keyword::VP, keyword::Encoding::ASCII)},
45 {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)},
46 };
47
48 namespace
49 {
toHex(size_t c)50 constexpr auto toHex(size_t c)
51 {
52 constexpr auto map = "0123456789abcdef";
53 return map[c];
54 }
55 } // namespace
56
57 /*readUInt16LE: Read 2 bytes LE data*/
readUInt16LE(Binary::const_iterator iterator)58 static LE2ByteData readUInt16LE(Binary::const_iterator iterator)
59 {
60 LE2ByteData lowByte = *iterator;
61 LE2ByteData highByte = *(iterator + 1);
62 lowByte |= (highByte << 8);
63 return lowByte;
64 }
65
getVtocOffset() const66 RecordOffset Impl::getVtocOffset() const
67 {
68 auto vpdPtr = vpd.cbegin();
69 std::advance(vpdPtr, offsets::VTOC_PTR);
70 // Get VTOC Offset
71 auto vtocOffset = readUInt16LE(vpdPtr);
72
73 return vtocOffset;
74 }
75
76 #ifdef IPZ_PARSER
vhdrEccCheck()77 int Impl::vhdrEccCheck()
78 {
79 int rc = eccStatus::SUCCESS;
80 auto vpdPtr = vpd.cbegin();
81
82 auto l_status = vpdecc_check_data(
83 const_cast<uint8_t*>(&vpdPtr[offsets::VHDR_RECORD]),
84 lengths::VHDR_RECORD_LENGTH,
85 const_cast<uint8_t*>(&vpdPtr[offsets::VHDR_ECC]),
86 lengths::VHDR_ECC_LENGTH);
87 if (l_status == VPD_ECC_CORRECTABLE_DATA)
88 {
89 try
90 {
91 if (vpdFileStream.is_open())
92 {
93 vpdFileStream.seekp(vpdStartOffset + offsets::VHDR_RECORD,
94 std::ios::beg);
95 vpdFileStream.write(
96 reinterpret_cast<const char*>(&vpd[offsets::VHDR_RECORD]),
97 lengths::VHDR_RECORD_LENGTH);
98 }
99 else
100 {
101 std::cerr << "File not open";
102 rc = eccStatus::FAILED;
103 }
104 }
105 catch (const std::fstream::failure& e)
106 {
107 std::cout << "Error while operating on file with exception:"
108 << e.what();
109 rc = eccStatus::FAILED;
110 }
111 }
112 else if (l_status != VPD_ECC_OK)
113 {
114 rc = eccStatus::FAILED;
115 }
116
117 return rc;
118 }
119
vtocEccCheck()120 int Impl::vtocEccCheck()
121 {
122 int rc = eccStatus::SUCCESS;
123 // Use another pointer to get ECC information from VHDR,
124 // actual pointer is pointing to VTOC data
125
126 auto vpdPtr = vpd.cbegin();
127
128 // Get VTOC Offset
129 auto vtocOffset = getVtocOffset();
130
131 // Get the VTOC Length
132 std::advance(vpdPtr, offsets::VTOC_PTR + sizeof(RecordOffset));
133 auto vtocLength = readUInt16LE(vpdPtr);
134
135 // Get the ECC Offset
136 std::advance(vpdPtr, sizeof(RecordLength));
137 auto vtocECCOffset = readUInt16LE(vpdPtr);
138
139 // Get the ECC length
140 std::advance(vpdPtr, sizeof(ECCOffset));
141 auto vtocECCLength = readUInt16LE(vpdPtr);
142
143 // Reset pointer to start of the vpd,
144 // so that Offset will point to correct address
145 vpdPtr = vpd.cbegin();
146 auto l_status = vpdecc_check_data(
147 const_cast<uint8_t*>(&vpdPtr[vtocOffset]), vtocLength,
148 const_cast<uint8_t*>(&vpdPtr[vtocECCOffset]), vtocECCLength);
149 if (l_status == VPD_ECC_CORRECTABLE_DATA)
150 {
151 try
152 {
153 if (vpdFileStream.is_open())
154 {
155 vpdFileStream.seekp(vpdStartOffset + vtocOffset, std::ios::beg);
156 vpdFileStream.write(
157 reinterpret_cast<const char*>(&vpdPtr[vtocOffset]),
158 vtocLength);
159 }
160 else
161 {
162 std::cerr << "File not open";
163 rc = eccStatus::FAILED;
164 }
165 }
166 catch (const std::fstream::failure& e)
167 {
168 std::cout << "Error while operating on file with exception "
169 << e.what();
170 rc = eccStatus::FAILED;
171 }
172 }
173 else if (l_status != VPD_ECC_OK)
174 {
175 rc = eccStatus::FAILED;
176 }
177
178 return rc;
179 }
180
recordEccCheck(Binary::const_iterator iterator)181 int Impl::recordEccCheck(Binary::const_iterator iterator)
182 {
183 int rc = eccStatus::SUCCESS;
184
185 auto recordOffset = readUInt16LE(iterator);
186
187 std::advance(iterator, sizeof(RecordOffset));
188 auto recordLength = readUInt16LE(iterator);
189
190 std::advance(iterator, sizeof(RecordLength));
191 auto eccOffset = readUInt16LE(iterator);
192
193 std::advance(iterator, sizeof(ECCOffset));
194 auto eccLength = readUInt16LE(iterator);
195
196 if (eccLength == 0 || eccOffset == 0)
197 {
198 throw(VpdEccException(
199 "Could not find ECC's offset or Length for Record:"));
200 }
201
202 if (recordOffset == 0 || recordLength == 0)
203 {
204 throw(VpdDataException("Could not find VPD record offset or VPD record "
205 "length for Record:"));
206 }
207
208 auto vpdPtr = vpd.cbegin();
209
210 auto l_status = vpdecc_check_data(
211 const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength,
212 const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength);
213 if (l_status == VPD_ECC_CORRECTABLE_DATA)
214 {
215 try
216 {
217 if (vpdFileStream.is_open())
218 {
219 vpdFileStream.seekp(vpdStartOffset + recordOffset,
220 std::ios::beg);
221 vpdFileStream.write(
222 reinterpret_cast<const char*>(&vpdPtr[recordOffset]),
223 recordLength);
224 }
225 else
226 {
227 std::cerr << "File not open";
228 rc = eccStatus::FAILED;
229 }
230 }
231 catch (const std::fstream::failure& e)
232 {
233 std::cout << "Error while operating on file with exception "
234 << e.what();
235 rc = eccStatus::FAILED;
236 }
237 }
238 else if (l_status != VPD_ECC_OK)
239 {
240 rc = eccStatus::FAILED;
241 }
242
243 return rc;
244 }
245 #endif
246
checkHeader()247 void Impl::checkHeader()
248 {
249 if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
250 {
251 throw(VpdDataException("Malformed VPD"));
252 }
253 else
254 {
255 auto iterator = vpd.cbegin();
256 std::advance(iterator, offsets::VHDR);
257 auto stop = std::next(iterator, lengths::RECORD_NAME);
258 std::string record(iterator, stop);
259 if ("VHDR" != record)
260 {
261 throw(VpdDataException("VHDR record not found"));
262 }
263
264 #ifdef IPZ_PARSER
265 // Check ECC
266 int rc = eccStatus::FAILED;
267 rc = vhdrEccCheck();
268 if (rc != eccStatus::SUCCESS)
269 {
270 throw(VpdEccException("ERROR: VHDR ECC check Failed"));
271 }
272 #endif
273 }
274 }
275
readTOC(Binary::const_iterator & iterator)276 std::size_t Impl::readTOC(Binary::const_iterator& iterator)
277 {
278 // The offset to VTOC could be 1 or 2 bytes long
279 RecordOffset vtocOffset = getVtocOffset();
280
281 // Got the offset to VTOC, skip past record header and keyword header
282 // to get to the record name.
283 std::advance(iterator, vtocOffset + sizeof(RecordId) + sizeof(RecordSize) +
284 // Skip past the RT keyword, which contains
285 // the record name.
286 lengths::KW_NAME + sizeof(KwSize));
287
288 auto stop = std::next(iterator, lengths::RECORD_NAME);
289 std::string record(iterator, stop);
290 if ("VTOC" != record)
291 {
292 throw(VpdDataException("VTOC record not found"));
293 }
294
295 #ifdef IPZ_PARSER
296 // Check ECC
297 int rc = eccStatus::FAILED;
298 rc = vtocEccCheck();
299 if (rc != eccStatus::SUCCESS)
300 {
301 throw(VpdEccException("ERROR: VTOC ECC check Failed"));
302 }
303 #endif
304 // VTOC record name is good, now read through the TOC, stored in the PT
305 // PT keyword; vpdBuffer is now pointing at the first character of the
306 // name 'VTOC', jump to PT data.
307 // Skip past record name and KW name, 'PT'
308 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
309 // Note size of PT
310 std::size_t ptLen = *iterator;
311 // Skip past PT size
312 std::advance(iterator, sizeof(KwSize));
313
314 // length of PT keyword
315 return ptLen;
316 }
317
readPT(Binary::const_iterator iterator,std::size_t ptLength)318 internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
319 std::size_t ptLength)
320 {
321 internal::OffsetList offsets{};
322
323 auto end = iterator;
324 std::advance(end, ptLength);
325
326 // Look at each entry in the PT keyword. In the entry,
327 // we care only about the record offset information.
328 while (iterator < end)
329 {
330 #ifdef IPZ_PARSER
331 auto iteratorToRecName = iterator;
332 #endif
333 // Skip record name and record type
334 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
335
336 // Get record offset
337 auto offset = readUInt16LE(iterator);
338 offsets.push_back(offset);
339
340 #ifdef IPZ_PARSER
341 std::string recordName(iteratorToRecName,
342 iteratorToRecName + lengths::RECORD_NAME);
343
344 try
345 {
346 // Verify the ECC for this Record
347 int rc = recordEccCheck(iterator);
348
349 if (rc != eccStatus::SUCCESS)
350 {
351 std::string errorMsg = std::string(
352 "ERROR: ECC check did not pass for the "
353 "Record:");
354 throw(VpdEccException(errorMsg));
355 }
356 }
357 catch (const VpdEccException& ex)
358 {
359 inventory::PelAdditionalData additionalData{};
360 additionalData.emplace("DESCRIPTION",
361 std::string{ex.what()} + recordName);
362 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
363 createPEL(additionalData, PelSeverity::WARNING,
364 errIntfForEccCheckFail, nullptr);
365 }
366 catch (const VpdDataException& ex)
367 {
368 inventory::PelAdditionalData additionalData{};
369 additionalData.emplace("DESCRIPTION",
370 std::string{ex.what()} + recordName);
371 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
372 createPEL(additionalData, PelSeverity::WARNING,
373 errIntfForInvalidVPD, nullptr);
374 }
375
376 #endif
377
378 // Jump record size, record length, ECC offset and ECC length
379 std::advance(iterator, sizeof(RecordOffset) + sizeof(RecordLength) +
380 sizeof(ECCOffset) + sizeof(ECCLength));
381 }
382
383 return offsets;
384 }
385
processRecord(std::size_t recordOffset)386 void Impl::processRecord(std::size_t recordOffset)
387 {
388 // Jump to record name
389 auto nameOffset = recordOffset + sizeof(RecordId) + sizeof(RecordSize) +
390 // Skip past the RT keyword, which contains
391 // the record name.
392 lengths::KW_NAME + sizeof(KwSize);
393 // Get record name
394 auto iterator = vpd.cbegin();
395 std::advance(iterator, nameOffset);
396
397 std::string name(iterator, iterator + lengths::RECORD_NAME);
398
399 #ifndef IPZ_PARSER
400 if (supportedRecords.end() != supportedRecords.find(name))
401 {
402 #endif
403 // If it's a record we're interested in, proceed to find
404 // contained keywords and their values.
405 std::advance(iterator, lengths::RECORD_NAME);
406
407 #ifdef IPZ_PARSER
408
409 // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
410 std::advance(iterator, -(lengths::KW_NAME + sizeof(KwSize) +
411 lengths::RECORD_NAME));
412 #endif
413 auto kwMap = readKeywords(iterator);
414 // Add entry for this record (and contained keyword:value pairs)
415 // to the parsed vpd output.
416 out.emplace(std::move(name), std::move(kwMap));
417
418 #ifndef IPZ_PARSER
419 }
420 #endif
421 }
422
readKwData(const internal::KeywordInfo & keyword,std::size_t dataLength,Binary::const_iterator iterator)423 std::string Impl::readKwData(const internal::KeywordInfo& keyword,
424 std::size_t dataLength,
425 Binary::const_iterator iterator)
426 {
427 using namespace openpower::vpd;
428 switch (std::get<keyword::Encoding>(keyword))
429 {
430 case keyword::Encoding::ASCII:
431 {
432 auto stop = std::next(iterator, dataLength);
433 return std::string(iterator, stop);
434 }
435
436 case keyword::Encoding::RAW:
437 {
438 auto stop = std::next(iterator, dataLength);
439 std::string data(iterator, stop);
440 std::string result{};
441 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
442 result += toHex(c >> 4);
443 result += toHex(c & 0x0F);
444 });
445 return result;
446 }
447
448 case keyword::Encoding::MB:
449 {
450 // MB is BuildDate, represent as
451 // 1997-01-01-08:30:00
452 // <year>-<month>-<day>-<hour>:<min>:<sec>
453 auto stop = std::next(iterator, MB_LEN_BYTES);
454 std::string data(iterator, stop);
455 std::string result;
456 result.reserve(MB_LEN_BYTES);
457 auto strItr = data.cbegin();
458 std::advance(strItr, 1);
459 std::for_each(strItr, data.cend(), [&result](size_t c) {
460 result += toHex(c >> 4);
461 result += toHex(c & 0x0F);
462 });
463
464 result.insert(MB_YEAR_END, 1, '-');
465 result.insert(MB_MONTH_END, 1, '-');
466 result.insert(MB_DAY_END, 1, '-');
467 result.insert(MB_HOUR_END, 1, ':');
468 result.insert(MB_MIN_END, 1, ':');
469
470 return result;
471 }
472
473 case keyword::Encoding::B1:
474 {
475 // B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
476 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
477 std::string data(iterator, stop);
478 std::string result{};
479 auto strItr = data.cbegin();
480 size_t firstDigit = *strItr;
481 result += toHex(firstDigit >> 4);
482 result += toHex(firstDigit & 0x0F);
483 std::advance(strItr, 1);
484 std::for_each(strItr, data.cend(), [&result](size_t c) {
485 result += ":";
486 result += toHex(c >> 4);
487 result += toHex(c & 0x0F);
488 });
489 return result;
490 }
491
492 case keyword::Encoding::UD:
493 {
494 // UD, the UUID info, represented as
495 // 123e4567-e89b-12d3-a456-426655440000
496 //<time_low>-<time_mid>-<time hi and version>
497 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id>
498 auto stop = std::next(iterator, UUID_LEN_BYTES);
499 std::string data(iterator, stop);
500 std::string result{};
501 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
502 result += toHex(c >> 4);
503 result += toHex(c & 0x0F);
504 });
505 result.insert(UUID_TIME_LOW_END, 1, '-');
506 result.insert(UUID_TIME_MID_END, 1, '-');
507 result.insert(UUID_TIME_HIGH_END, 1, '-');
508 result.insert(UUID_CLK_SEQ_END, 1, '-');
509
510 return result;
511 }
512 default:
513 break;
514 }
515
516 return {};
517 }
518
readKeywords(Binary::const_iterator iterator)519 internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
520 {
521 internal::KeywordMap map{};
522 while (true)
523 {
524 // Note keyword name
525 std::string kw(iterator, iterator + lengths::KW_NAME);
526 if (LAST_KW == kw)
527 {
528 // We're done
529 break;
530 }
531 // Check if the Keyword is '#kw'
532 char kwNameStart = *iterator;
533
534 // Jump past keyword name
535 std::advance(iterator, lengths::KW_NAME);
536
537 std::size_t length;
538 std::size_t lengthHighByte;
539 if (POUND_KW == kwNameStart)
540 {
541 // Note keyword data length
542 length = *iterator;
543 lengthHighByte = *(iterator + 1);
544 length |= (lengthHighByte << 8);
545
546 // Jump past 2Byte keyword length
547 std::advance(iterator, sizeof(PoundKwSize));
548 }
549 else
550 {
551 // Note keyword data length
552 length = *iterator;
553
554 // Jump past keyword length
555 std::advance(iterator, sizeof(KwSize));
556 }
557
558 // Pointing to keyword data now
559 #ifndef IPZ_PARSER
560 if (supportedKeywords.end() != supportedKeywords.find(kw))
561 {
562 // Keyword is of interest to us
563 std::string data = readKwData((supportedKeywords.find(kw))->second,
564 length, iterator);
565 map.emplace(std::move(kw), std::move(data));
566 }
567
568 #else
569 // support all the Keywords
570 auto stop = std::next(iterator, length);
571 std::string kwdata(iterator, stop);
572 map.emplace(std::move(kw), std::move(kwdata));
573
574 #endif
575 // Jump past keyword data length
576 std::advance(iterator, length);
577 }
578
579 return map;
580 }
581
run()582 Store Impl::run()
583 {
584 // Check if the VHDR record is present
585 checkHeader();
586
587 auto iterator = vpd.cbegin();
588
589 // Read the table of contents record
590 std::size_t ptLen = readTOC(iterator);
591
592 // Read the table of contents record, to get offsets
593 // to other records.
594 auto offsets = readPT(iterator, ptLen);
595 for (const auto& offset : offsets)
596 {
597 processRecord(offset);
598 }
599 // Return a Store object, which has interfaces to
600 // access parsed VPD by record:keyword
601 return Store(std::move(out));
602 }
603
checkVPDHeader()604 void Impl::checkVPDHeader()
605 {
606 // Check if the VHDR record is present and is valid
607 checkHeader();
608 }
609
readKwFromHw(const std::string & record,const std::string & keyword)610 std::string Impl::readKwFromHw(const std::string& record,
611 const std::string& keyword)
612 {
613 // Check if the VHDR record is present
614 checkHeader();
615
616 auto iterator = vpd.cbegin();
617
618 // Read the table of contents record
619 std::size_t ptLen = readTOC(iterator);
620
621 // Read the table of contents record, to get offsets
622 // to other records.
623 auto offsets = readPT(iterator, ptLen);
624 for (const auto& offset : offsets)
625 {
626 // Jump to record name
627 auto nameOffset = offset + sizeof(RecordId) + sizeof(RecordSize) +
628 // Skip past the RT keyword, which contains
629 // the record name.
630 lengths::KW_NAME + sizeof(KwSize);
631 // Get record name
632 auto iterator = vpd.cbegin();
633 std::advance(iterator, nameOffset);
634
635 std::string name(iterator, iterator + lengths::RECORD_NAME);
636 if (name != record)
637 {
638 continue;
639 }
640 else
641 {
642 processRecord(offset);
643 const auto& itr = out.find(record);
644 if (itr != out.end())
645 {
646 const auto& kwValItr = (itr->second).find(keyword);
647 if (kwValItr != (itr->second).end())
648 {
649 return kwValItr->second;
650 }
651 else
652 {
653 return "";
654 }
655 }
656 }
657 }
658 return "";
659 }
660
661 } // namespace parser
662 } // namespace vpd
663 } // namespace openpower
664