blob: c7036b7b8277ae21d638421daf99de51879c6649 [file] [log] [blame]
Patrick Venturec83c4dc2018-11-01 16:29:18 -07001#include "impl.hpp"
2
SunnySrivastava198419be6d32020-03-03 07:21:45 -06003#include "const.hpp"
Patrick Venturec83c4dc2018-11-01 16:29:18 -07004#include "defines.hpp"
Santosh Puranikbd011b22020-01-23 04:05:25 -06005#include "utils.hpp"
Patrick Venturec83c4dc2018-11-01 16:29:18 -07006
7#include <algorithm>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -06008#include <exception>
Patrick Venturec83c4dc2018-11-01 16:29:18 -07009#include <iomanip>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060010#include <iostream>
11#include <iterator>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070012#include <sstream>
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060013#include <tuple>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070014#include <unordered_map>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060015
Alpana Kumaric0aeac32019-11-28 05:20:10 -060016#include "vpdecc/vpdecc.h"
17
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060018namespace openpower
19{
20namespace vpd
21{
22namespace parser
23{
SunnySrivastava198419be6d32020-03-03 07:21:45 -060024using namespace openpower::vpd::constants;
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060025
Patrick Venturec83c4dc2018-11-01 16:29:18 -070026static const std::unordered_map<std::string, Record> supportedRecords = {
27 {"VINI", Record::VINI}, {"OPFR", Record::OPFR}, {"OSYS", Record::OSYS}};
Deepak Kodihallia1143462016-11-24 06:28:45 -060028
Patrick Venturec83c4dc2018-11-01 16:29:18 -070029static const std::unordered_map<std::string, internal::KeywordInfo>
30 supportedKeywords = {
31 {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)},
32 {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)},
33 {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)},
34 {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)},
35 {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)},
36 {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)},
37 {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)},
George Liuee79ca82019-07-12 11:05:33 +080038 {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::MB)},
Patrick Venturec83c4dc2018-11-01 16:29:18 -070039 {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)},
40 {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)},
41 {"VP", std::make_tuple(record::Keyword::VP, keyword::Encoding::ASCII)},
42 {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)},
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060043};
44
Alpana Kumaric0aeac32019-11-28 05:20:10 -060045namespace
46{
47constexpr auto toHex(size_t c)
48{
49 constexpr auto map = "0123456789abcdef";
50 return map[c];
51}
52} // namespace
53
54/*readUInt16LE: Read 2 bytes LE data*/
55static LE2ByteData readUInt16LE(Binary::const_iterator iterator)
56{
57 LE2ByteData lowByte = *iterator;
58 LE2ByteData highByte = *(iterator + 1);
59 lowByte |= (highByte << 8);
60 return lowByte;
61}
62
63RecordOffset Impl::getVtocOffset() const
64{
65 auto vpdPtr = vpd.cbegin();
66 std::advance(vpdPtr, offsets::VTOC_PTR);
67 // Get VTOC Offset
68 auto vtocOffset = readUInt16LE(vpdPtr);
69
70 return vtocOffset;
71}
72
73#ifdef IPZ_PARSER
74
75int Impl::vhdrEccCheck() const
76{
77 int rc = eccStatus::SUCCESS;
78 auto vpdPtr = vpd.cbegin();
79
80 auto l_status =
81 vpdecc_check_data(const_cast<uint8_t*>(&vpdPtr[offsets::VHDR_RECORD]),
82 lengths::VHDR_RECORD_LENGTH,
83 const_cast<uint8_t*>(&vpdPtr[offsets::VHDR_ECC]),
84 lengths::VHDR_ECC_LENGTH);
85 if (l_status != VPD_ECC_OK)
86 {
87 rc = eccStatus::FAILED;
88 }
89
90 return rc;
91}
92
93int Impl::vtocEccCheck() const
94{
95 int rc = eccStatus::SUCCESS;
96 // Use another pointer to get ECC information from VHDR,
97 // actual pointer is pointing to VTOC data
98
99 auto vpdPtr = vpd.cbegin();
100
101 // Get VTOC Offset
102 auto vtocOffset = getVtocOffset();
103
104 // Get the VTOC Length
105 std::advance(vpdPtr, offsets::VTOC_PTR + sizeof(RecordOffset));
106 auto vtocLength = readUInt16LE(vpdPtr);
107
108 // Get the ECC Offset
109 std::advance(vpdPtr, sizeof(RecordLength));
110 auto vtocECCOffset = readUInt16LE(vpdPtr);
111
112 // Get the ECC length
113 std::advance(vpdPtr, sizeof(ECCOffset));
114 auto vtocECCLength = readUInt16LE(vpdPtr);
115
116 // Reset pointer to start of the vpd,
117 // so that Offset will point to correct address
118 vpdPtr = vpd.cbegin();
119 auto l_status = vpdecc_check_data(
120 const_cast<uint8_t*>(&vpdPtr[vtocOffset]), vtocLength,
121 const_cast<uint8_t*>(&vpdPtr[vtocECCOffset]), vtocECCLength);
122 if (l_status != VPD_ECC_OK)
123 {
124 rc = eccStatus::FAILED;
125 }
126
127 return rc;
128}
129
130int Impl::recordEccCheck(Binary::const_iterator iterator) const
131{
132 int rc = eccStatus::SUCCESS;
133
134 auto recordOffset = readUInt16LE(iterator);
135
136 std::advance(iterator, sizeof(RecordOffset));
137 auto recordLength = readUInt16LE(iterator);
138
139 std::advance(iterator, sizeof(RecordLength));
140 auto eccOffset = readUInt16LE(iterator);
141
142 std::advance(iterator, sizeof(ECCOffset));
143 auto eccLength = readUInt16LE(iterator);
144
145 if (eccLength == 0 || eccOffset == 0 || recordOffset == 0 ||
146 recordLength == 0)
147 {
148 throw std::runtime_error("Something went wrong. Could't find Record's "
149 "OR its ECC's offset and Length");
150 }
151
152 auto vpdPtr = vpd.cbegin();
153
154 auto l_status = vpdecc_check_data(
155 const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength,
156 const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength);
157 if (l_status != VPD_ECC_OK)
158 {
159 rc = eccStatus::FAILED;
160 }
161
162 return rc;
163}
164#endif
165
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600166void Impl::checkHeader() const
167{
168 if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
169 {
170 throw std::runtime_error("Malformed VPD");
171 }
172 else
173 {
174 auto iterator = vpd.cbegin();
175 std::advance(iterator, offsets::VHDR);
176 auto stop = std::next(iterator, lengths::RECORD_NAME);
177 std::string record(iterator, stop);
178 if ("VHDR" != record)
179 {
180 throw std::runtime_error("VHDR record not found");
181 }
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600182
183#ifdef IPZ_PARSER
184 // Check ECC
185 int rc = eccStatus::FAILED;
186 rc = vhdrEccCheck();
187 if (rc != eccStatus::SUCCESS)
188 {
189 throw std::runtime_error("ERROR: VHDR ECC check Failed");
190 }
191#endif
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600192 }
193}
194
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600195std::size_t Impl::readTOC(Binary::const_iterator& iterator) const
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600196{
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600197 // The offset to VTOC could be 1 or 2 bytes long
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600198 RecordOffset vtocOffset = getVtocOffset();
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600199
200 // Got the offset to VTOC, skip past record header and keyword header
201 // to get to the record name.
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700202 std::advance(iterator, vtocOffset + sizeof(RecordId) + sizeof(RecordSize) +
203 // Skip past the RT keyword, which contains
204 // the record name.
205 lengths::KW_NAME + sizeof(KwSize));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600206
207 auto stop = std::next(iterator, lengths::RECORD_NAME);
208 std::string record(iterator, stop);
209 if ("VTOC" != record)
210 {
211 throw std::runtime_error("VTOC record not found");
212 }
213
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600214#ifdef IPZ_PARSER
215 // Check ECC
216 int rc = eccStatus::FAILED;
217 rc = vtocEccCheck();
218 if (rc != eccStatus::SUCCESS)
219 {
220 throw std::runtime_error("ERROR: VTOC ECC check Failed");
221 }
222#endif
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600223 // VTOC record name is good, now read through the TOC, stored in the PT
224 // PT keyword; vpdBuffer is now pointing at the first character of the
225 // name 'VTOC', jump to PT data.
226 // Skip past record name and KW name, 'PT'
227 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
228 // Note size of PT
229 std::size_t ptLen = *iterator;
230 // Skip past PT size
231 std::advance(iterator, sizeof(KwSize));
232
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600233 // length of PT keyword
234 return ptLen;
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600235}
236
237internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
238 std::size_t ptLength) const
239{
240 internal::OffsetList offsets{};
241
242 auto end = iterator;
243 std::advance(end, ptLength);
244
245 // Look at each entry in the PT keyword. In the entry,
246 // we care only about the record offset information.
247 while (iterator < end)
248 {
249 // Skip record name and record type
250 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
251
252 // Get record offset
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600253 auto offset = readUInt16LE(iterator);
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600254 offsets.push_back(offset);
255
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600256#ifdef IPZ_PARSER
257 // Verify the ECC for this Record
258 int rc = recordEccCheck(iterator);
259
260 if (rc != eccStatus::SUCCESS)
261 {
262 throw std::runtime_error(
263 "ERROR: ECC check for one of the Record did not Pass.");
264 }
265#endif
266
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600267 // Jump record size, record length, ECC offset and ECC length
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600268 std::advance(iterator, sizeof(RecordOffset) + sizeof(RecordLength) +
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700269 sizeof(ECCOffset) + sizeof(ECCLength));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600270 }
271
272 return offsets;
273}
274
Deepak Kodihallia1143462016-11-24 06:28:45 -0600275void Impl::processRecord(std::size_t recordOffset)
276{
277 // Jump to record name
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700278 auto nameOffset = recordOffset + sizeof(RecordId) + sizeof(RecordSize) +
Deepak Kodihallia1143462016-11-24 06:28:45 -0600279 // Skip past the RT keyword, which contains
280 // the record name.
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700281 lengths::KW_NAME + sizeof(KwSize);
Deepak Kodihallia1143462016-11-24 06:28:45 -0600282 // Get record name
283 auto iterator = vpd.cbegin();
284 std::advance(iterator, nameOffset);
285
286 std::string name(iterator, iterator + lengths::RECORD_NAME);
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600287
Alpana Kumari26a74af2019-09-10 23:53:58 -0500288#ifndef IPZ_PARSER
Deepak Kodihallia1143462016-11-24 06:28:45 -0600289 if (supportedRecords.end() != supportedRecords.find(name))
290 {
Alpana Kumari26a74af2019-09-10 23:53:58 -0500291#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600292 // If it's a record we're interested in, proceed to find
293 // contained keywords and their values.
294 std::advance(iterator, lengths::RECORD_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500295
296#ifdef IPZ_PARSER
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600297
Alpana Kumari26a74af2019-09-10 23:53:58 -0500298 // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
299 std::advance(iterator, -(lengths::KW_NAME + sizeof(KwSize) +
300 lengths::RECORD_NAME));
301#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600302 auto kwMap = readKeywords(iterator);
303 // Add entry for this record (and contained keyword:value pairs)
304 // to the parsed vpd output.
305 out.emplace(std::move(name), std::move(kwMap));
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600306
Alpana Kumari26a74af2019-09-10 23:53:58 -0500307#ifndef IPZ_PARSER
Deepak Kodihallia1143462016-11-24 06:28:45 -0600308 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500309#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600310}
311
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600312std::string Impl::readKwData(const internal::KeywordInfo& keyword,
313 std::size_t dataLength,
314 Binary::const_iterator iterator)
315{
Santosh Puranikbd011b22020-01-23 04:05:25 -0600316 using namespace openpower::vpd;
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600317 switch (std::get<keyword::Encoding>(keyword))
318 {
319 case keyword::Encoding::ASCII:
320 {
321 auto stop = std::next(iterator, dataLength);
322 return std::string(iterator, stop);
323 }
324
325 case keyword::Encoding::RAW:
326 {
327 auto stop = std::next(iterator, dataLength);
328 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700329 std::string result{};
330 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
331 result += toHex(c >> 4);
332 result += toHex(c & 0x0F);
333 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600334 return result;
335 }
336
George Liuee79ca82019-07-12 11:05:33 +0800337 case keyword::Encoding::MB:
338 {
339 // MB is BuildDate, represent as
340 // 1997-01-01-08:30:00
341 // <year>-<month>-<day>-<hour>:<min>:<sec>
342 auto stop = std::next(iterator, MB_LEN_BYTES);
343 std::string data(iterator, stop);
344 std::string result;
345 result.reserve(MB_LEN_BYTES);
346 auto strItr = data.cbegin();
347 std::advance(strItr, 1);
348 std::for_each(strItr, data.cend(), [&result](size_t c) {
349 result += toHex(c >> 4);
350 result += toHex(c & 0x0F);
351 });
352
353 result.insert(MB_YEAR_END, 1, '-');
354 result.insert(MB_MONTH_END, 1, '-');
355 result.insert(MB_DAY_END, 1, '-');
356 result.insert(MB_HOUR_END, 1, ':');
357 result.insert(MB_MIN_END, 1, ':');
358
359 return result;
360 }
361
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600362 case keyword::Encoding::B1:
363 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700364 // B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600365 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
366 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700367 std::string result{};
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600368 auto strItr = data.cbegin();
369 size_t firstDigit = *strItr;
370 result += toHex(firstDigit >> 4);
371 result += toHex(firstDigit & 0x0F);
372 std::advance(strItr, 1);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700373 std::for_each(strItr, data.cend(), [&result](size_t c) {
374 result += ":";
375 result += toHex(c >> 4);
376 result += toHex(c & 0x0F);
377 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600378 return result;
379 }
380
Dinesh Chinaric576b482017-07-17 16:34:10 -0500381 case keyword::Encoding::UD:
382 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700383 // UD, the UUID info, represented as
384 // 123e4567-e89b-12d3-a456-426655440000
Dinesh Chinaric576b482017-07-17 16:34:10 -0500385 //<time_low>-<time_mid>-<time hi and version>
386 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id>
387 auto stop = std::next(iterator, UUID_LEN_BYTES);
388 std::string data(iterator, stop);
389 std::string result{};
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700390 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
391 result += toHex(c >> 4);
392 result += toHex(c & 0x0F);
393 });
Dinesh Chinaric576b482017-07-17 16:34:10 -0500394 result.insert(UUID_TIME_LOW_END, 1, '-');
395 result.insert(UUID_TIME_MID_END, 1, '-');
396 result.insert(UUID_TIME_HIGH_END, 1, '-');
397 result.insert(UUID_CLK_SEQ_END, 1, '-');
398
399 return result;
400 }
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600401 default:
402 break;
403 }
404
405 return {};
406}
407
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600408internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
409{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700410 internal::KeywordMap map{};
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600411 while (true)
412 {
413 // Note keyword name
414 std::string kw(iterator, iterator + lengths::KW_NAME);
415 if (LAST_KW == kw)
416 {
417 // We're done
418 break;
419 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500420 // Check if the Keyword is '#kw'
421 char kwNameStart = *iterator;
422
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600423 // Jump past keyword name
424 std::advance(iterator, lengths::KW_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500425
426 std::size_t length;
427 std::size_t lengthHighByte;
428 if (POUND_KW == kwNameStart)
429 {
430 // Note keyword data length
431 length = *iterator;
432 lengthHighByte = *(iterator + 1);
433 length |= (lengthHighByte << 8);
434
435 // Jump past 2Byte keyword length
436 std::advance(iterator, sizeof(PoundKwSize));
437 }
438 else
439 {
440 // Note keyword data length
441 length = *iterator;
442
443 // Jump past keyword length
444 std::advance(iterator, sizeof(KwSize));
445 }
446
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600447 // Pointing to keyword data now
Alpana Kumari26a74af2019-09-10 23:53:58 -0500448#ifndef IPZ_PARSER
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600449 if (supportedKeywords.end() != supportedKeywords.find(kw))
450 {
451 // Keyword is of interest to us
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700452 std::string data = readKwData((supportedKeywords.find(kw))->second,
453 length, iterator);
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600454 map.emplace(std::move(kw), std::move(data));
455 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500456
457#else
458 // support all the Keywords
459 auto stop = std::next(iterator, length);
460 std::string kwdata(iterator, stop);
461 map.emplace(std::move(kw), std::move(kwdata));
462
463#endif
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600464 // Jump past keyword data length
465 std::advance(iterator, length);
466 }
467
468 return map;
469}
470
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600471Store Impl::run()
472{
473 // Check if the VHDR record is present
474 checkHeader();
475
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600476 auto iterator = vpd.cbegin();
477
478 // Read the table of contents record
479 std::size_t ptLen = readTOC(iterator);
480
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600481 // Read the table of contents record, to get offsets
482 // to other records.
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600483 auto offsets = readPT(iterator, ptLen);
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600484 for (const auto& offset : offsets)
485 {
486 processRecord(offset);
487 }
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600488 // Return a Store object, which has interfaces to
489 // access parsed VPD by record:keyword
490 return Store(std::move(out));
491}
492
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600493void Impl::checkVPDHeader()
494{
495 // Check if the VHDR record is present and is valid
496 checkHeader();
497}
498
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600499} // namespace parser
500} // namespace vpd
501} // namespace openpower