blob: d647d8b1c784dbc2adeef79404745354add051ee [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
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600195internal::OffsetList Impl::readTOC() const
196{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700197 internal::OffsetList offsets{};
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600198
199 // The offset to VTOC could be 1 or 2 bytes long
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600200 RecordOffset vtocOffset = getVtocOffset();
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600201
202 // Got the offset to VTOC, skip past record header and keyword header
203 // to get to the record name.
204 auto iterator = vpd.cbegin();
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700205 std::advance(iterator, vtocOffset + sizeof(RecordId) + sizeof(RecordSize) +
206 // Skip past the RT keyword, which contains
207 // the record name.
208 lengths::KW_NAME + sizeof(KwSize));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600209
210 auto stop = std::next(iterator, lengths::RECORD_NAME);
211 std::string record(iterator, stop);
212 if ("VTOC" != record)
213 {
214 throw std::runtime_error("VTOC record not found");
215 }
216
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600217#ifdef IPZ_PARSER
218 // Check ECC
219 int rc = eccStatus::FAILED;
220 rc = vtocEccCheck();
221 if (rc != eccStatus::SUCCESS)
222 {
223 throw std::runtime_error("ERROR: VTOC ECC check Failed");
224 }
225#endif
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600226 // VTOC record name is good, now read through the TOC, stored in the PT
227 // PT keyword; vpdBuffer is now pointing at the first character of the
228 // name 'VTOC', jump to PT data.
229 // Skip past record name and KW name, 'PT'
230 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
231 // Note size of PT
232 std::size_t ptLen = *iterator;
233 // Skip past PT size
234 std::advance(iterator, sizeof(KwSize));
235
236 // vpdBuffer is now pointing to PT data
237 return readPT(iterator, ptLen);
238}
239
240internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
241 std::size_t ptLength) const
242{
243 internal::OffsetList offsets{};
244
245 auto end = iterator;
246 std::advance(end, ptLength);
247
248 // Look at each entry in the PT keyword. In the entry,
249 // we care only about the record offset information.
250 while (iterator < end)
251 {
252 // Skip record name and record type
253 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
254
255 // Get record offset
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600256 auto offset = readUInt16LE(iterator);
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600257 offsets.push_back(offset);
258
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600259#ifdef IPZ_PARSER
260 // Verify the ECC for this Record
261 int rc = recordEccCheck(iterator);
262
263 if (rc != eccStatus::SUCCESS)
264 {
265 throw std::runtime_error(
266 "ERROR: ECC check for one of the Record did not Pass.");
267 }
268#endif
269
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600270 // Jump record size, record length, ECC offset and ECC length
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600271 std::advance(iterator, sizeof(RecordOffset) + sizeof(RecordLength) +
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700272 sizeof(ECCOffset) + sizeof(ECCLength));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600273 }
274
275 return offsets;
276}
277
Deepak Kodihallia1143462016-11-24 06:28:45 -0600278void Impl::processRecord(std::size_t recordOffset)
279{
280 // Jump to record name
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700281 auto nameOffset = recordOffset + sizeof(RecordId) + sizeof(RecordSize) +
Deepak Kodihallia1143462016-11-24 06:28:45 -0600282 // Skip past the RT keyword, which contains
283 // the record name.
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700284 lengths::KW_NAME + sizeof(KwSize);
Deepak Kodihallia1143462016-11-24 06:28:45 -0600285 // Get record name
286 auto iterator = vpd.cbegin();
287 std::advance(iterator, nameOffset);
288
289 std::string name(iterator, iterator + lengths::RECORD_NAME);
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600290
Alpana Kumari26a74af2019-09-10 23:53:58 -0500291#ifndef IPZ_PARSER
Deepak Kodihallia1143462016-11-24 06:28:45 -0600292 if (supportedRecords.end() != supportedRecords.find(name))
293 {
Alpana Kumari26a74af2019-09-10 23:53:58 -0500294#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600295 // If it's a record we're interested in, proceed to find
296 // contained keywords and their values.
297 std::advance(iterator, lengths::RECORD_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500298
299#ifdef IPZ_PARSER
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600300
Alpana Kumari26a74af2019-09-10 23:53:58 -0500301 // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
302 std::advance(iterator, -(lengths::KW_NAME + sizeof(KwSize) +
303 lengths::RECORD_NAME));
304#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600305 auto kwMap = readKeywords(iterator);
306 // Add entry for this record (and contained keyword:value pairs)
307 // to the parsed vpd output.
308 out.emplace(std::move(name), std::move(kwMap));
Alpana Kumaric0aeac32019-11-28 05:20:10 -0600309
Alpana Kumari26a74af2019-09-10 23:53:58 -0500310#ifndef IPZ_PARSER
Deepak Kodihallia1143462016-11-24 06:28:45 -0600311 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500312#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600313}
314
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600315std::string Impl::readKwData(const internal::KeywordInfo& keyword,
316 std::size_t dataLength,
317 Binary::const_iterator iterator)
318{
Santosh Puranikbd011b22020-01-23 04:05:25 -0600319 using namespace openpower::vpd;
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600320 switch (std::get<keyword::Encoding>(keyword))
321 {
322 case keyword::Encoding::ASCII:
323 {
324 auto stop = std::next(iterator, dataLength);
325 return std::string(iterator, stop);
326 }
327
328 case keyword::Encoding::RAW:
329 {
330 auto stop = std::next(iterator, dataLength);
331 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700332 std::string result{};
333 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
334 result += toHex(c >> 4);
335 result += toHex(c & 0x0F);
336 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600337 return result;
338 }
339
George Liuee79ca82019-07-12 11:05:33 +0800340 case keyword::Encoding::MB:
341 {
342 // MB is BuildDate, represent as
343 // 1997-01-01-08:30:00
344 // <year>-<month>-<day>-<hour>:<min>:<sec>
345 auto stop = std::next(iterator, MB_LEN_BYTES);
346 std::string data(iterator, stop);
347 std::string result;
348 result.reserve(MB_LEN_BYTES);
349 auto strItr = data.cbegin();
350 std::advance(strItr, 1);
351 std::for_each(strItr, data.cend(), [&result](size_t c) {
352 result += toHex(c >> 4);
353 result += toHex(c & 0x0F);
354 });
355
356 result.insert(MB_YEAR_END, 1, '-');
357 result.insert(MB_MONTH_END, 1, '-');
358 result.insert(MB_DAY_END, 1, '-');
359 result.insert(MB_HOUR_END, 1, ':');
360 result.insert(MB_MIN_END, 1, ':');
361
362 return result;
363 }
364
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600365 case keyword::Encoding::B1:
366 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700367 // B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600368 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
369 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700370 std::string result{};
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600371 auto strItr = data.cbegin();
372 size_t firstDigit = *strItr;
373 result += toHex(firstDigit >> 4);
374 result += toHex(firstDigit & 0x0F);
375 std::advance(strItr, 1);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700376 std::for_each(strItr, data.cend(), [&result](size_t c) {
377 result += ":";
378 result += toHex(c >> 4);
379 result += toHex(c & 0x0F);
380 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600381 return result;
382 }
383
Dinesh Chinaric576b482017-07-17 16:34:10 -0500384 case keyword::Encoding::UD:
385 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700386 // UD, the UUID info, represented as
387 // 123e4567-e89b-12d3-a456-426655440000
Dinesh Chinaric576b482017-07-17 16:34:10 -0500388 //<time_low>-<time_mid>-<time hi and version>
389 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id>
390 auto stop = std::next(iterator, UUID_LEN_BYTES);
391 std::string data(iterator, stop);
392 std::string result{};
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700393 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
394 result += toHex(c >> 4);
395 result += toHex(c & 0x0F);
396 });
Dinesh Chinaric576b482017-07-17 16:34:10 -0500397 result.insert(UUID_TIME_LOW_END, 1, '-');
398 result.insert(UUID_TIME_MID_END, 1, '-');
399 result.insert(UUID_TIME_HIGH_END, 1, '-');
400 result.insert(UUID_CLK_SEQ_END, 1, '-');
401
402 return result;
403 }
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600404 default:
405 break;
406 }
407
408 return {};
409}
410
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600411internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
412{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700413 internal::KeywordMap map{};
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600414 while (true)
415 {
416 // Note keyword name
417 std::string kw(iterator, iterator + lengths::KW_NAME);
418 if (LAST_KW == kw)
419 {
420 // We're done
421 break;
422 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500423 // Check if the Keyword is '#kw'
424 char kwNameStart = *iterator;
425
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600426 // Jump past keyword name
427 std::advance(iterator, lengths::KW_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500428
429 std::size_t length;
430 std::size_t lengthHighByte;
431 if (POUND_KW == kwNameStart)
432 {
433 // Note keyword data length
434 length = *iterator;
435 lengthHighByte = *(iterator + 1);
436 length |= (lengthHighByte << 8);
437
438 // Jump past 2Byte keyword length
439 std::advance(iterator, sizeof(PoundKwSize));
440 }
441 else
442 {
443 // Note keyword data length
444 length = *iterator;
445
446 // Jump past keyword length
447 std::advance(iterator, sizeof(KwSize));
448 }
449
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600450 // Pointing to keyword data now
Alpana Kumari26a74af2019-09-10 23:53:58 -0500451#ifndef IPZ_PARSER
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600452 if (supportedKeywords.end() != supportedKeywords.find(kw))
453 {
454 // Keyword is of interest to us
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700455 std::string data = readKwData((supportedKeywords.find(kw))->second,
456 length, iterator);
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600457 map.emplace(std::move(kw), std::move(data));
458 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500459
460#else
461 // support all the Keywords
462 auto stop = std::next(iterator, length);
463 std::string kwdata(iterator, stop);
464 map.emplace(std::move(kw), std::move(kwdata));
465
466#endif
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600467 // Jump past keyword data length
468 std::advance(iterator, length);
469 }
470
471 return map;
472}
473
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600474Store Impl::run()
475{
476 // Check if the VHDR record is present
477 checkHeader();
478
479 // Read the table of contents record, to get offsets
480 // to other records.
481 auto offsets = readTOC();
482 for (const auto& offset : offsets)
483 {
484 processRecord(offset);
485 }
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600486 // Return a Store object, which has interfaces to
487 // access parsed VPD by record:keyword
488 return Store(std::move(out));
489}
490
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600491} // namespace parser
492} // namespace vpd
493} // namespace openpower