blob: 35a0e0fe2fb44863773548ef5e46e2748e802d56 [file] [log] [blame]
Patrick Venturec83c4dc2018-11-01 16:29:18 -07001#include "impl.hpp"
2
3#include "defines.hpp"
4
5#include <algorithm>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -06006#include <exception>
Patrick Venturec83c4dc2018-11-01 16:29:18 -07007#include <iomanip>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -06008#include <iostream>
9#include <iterator>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070010#include <sstream>
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060011#include <tuple>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070012#include <unordered_map>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060013
14namespace openpower
15{
16namespace vpd
17{
18namespace parser
19{
20
Patrick Venturec83c4dc2018-11-01 16:29:18 -070021static const std::unordered_map<std::string, Record> supportedRecords = {
22 {"VINI", Record::VINI}, {"OPFR", Record::OPFR}, {"OSYS", Record::OSYS}};
Deepak Kodihallia1143462016-11-24 06:28:45 -060023
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060024static constexpr auto MAC_ADDRESS_LEN_BYTES = 6;
Deepak Kodihalli683bf722016-11-24 06:50:43 -060025static constexpr auto LAST_KW = "PF";
Alpana Kumari26a74af2019-09-10 23:53:58 -050026static constexpr auto POUND_KW = '#';
Dinesh Chinaric576b482017-07-17 16:34:10 -050027static constexpr auto UUID_LEN_BYTES = 16;
28static constexpr auto UUID_TIME_LOW_END = 8;
29static constexpr auto UUID_TIME_MID_END = 13;
30static constexpr auto UUID_TIME_HIGH_END = 18;
31static constexpr auto UUID_CLK_SEQ_END = 23;
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060032
George Liuee79ca82019-07-12 11:05:33 +080033static constexpr auto MB_RESULT_LEN = 19;
34static constexpr auto MB_LEN_BYTES = 8;
35static constexpr auto MB_YEAR_END = 4;
36static constexpr auto MB_MONTH_END = 7;
37static constexpr auto MB_DAY_END = 10;
38static constexpr auto MB_HOUR_END = 13;
39static constexpr auto MB_MIN_END = 16;
40
Patrick Venturec83c4dc2018-11-01 16:29:18 -070041static const std::unordered_map<std::string, internal::KeywordInfo>
42 supportedKeywords = {
43 {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)},
44 {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)},
45 {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)},
46 {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)},
47 {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)},
48 {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)},
49 {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)},
George Liuee79ca82019-07-12 11:05:33 +080050 {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::MB)},
Patrick Venturec83c4dc2018-11-01 16:29:18 -070051 {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)},
52 {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)},
53 {"VP", std::make_tuple(record::Keyword::VP, keyword::Encoding::ASCII)},
54 {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)},
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060055};
56
Deepak Kodihalli023112f2016-11-22 22:02:14 -060057namespace
58{
59
60using RecordId = uint8_t;
61using RecordOffset = uint16_t;
62using RecordSize = uint16_t;
63using RecordType = uint16_t;
64using RecordLength = uint16_t;
65using KwSize = uint8_t;
Alpana Kumari26a74af2019-09-10 23:53:58 -050066using PoundKwSize = uint16_t;
Deepak Kodihalli023112f2016-11-22 22:02:14 -060067using ECCOffset = uint16_t;
68using ECCLength = uint16_t;
69
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060070constexpr auto toHex(size_t c)
71{
72 constexpr auto map = "0123456789abcdef";
73 return map[c];
74}
75
Patrick Venturec83c4dc2018-11-01 16:29:18 -070076} // namespace
Deepak Kodihalli023112f2016-11-22 22:02:14 -060077
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060078namespace offsets
79{
80
81enum Offsets
82{
Deepak Kodihalli023112f2016-11-22 22:02:14 -060083 VHDR = 17,
84 VHDR_TOC_ENTRY = 29,
85 VTOC_PTR = 35,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060086};
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060087}
88
89namespace lengths
90{
91
92enum Lengths
93{
94 RECORD_NAME = 4,
Deepak Kodihalli023112f2016-11-22 22:02:14 -060095 KW_NAME = 2,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060096 RECORD_MIN = 44,
97};
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060098}
99
100void Impl::checkHeader() const
101{
102 if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
103 {
104 throw std::runtime_error("Malformed VPD");
105 }
106 else
107 {
108 auto iterator = vpd.cbegin();
109 std::advance(iterator, offsets::VHDR);
110 auto stop = std::next(iterator, lengths::RECORD_NAME);
111 std::string record(iterator, stop);
112 if ("VHDR" != record)
113 {
114 throw std::runtime_error("VHDR record not found");
115 }
116 }
117}
118
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600119internal::OffsetList Impl::readTOC() const
120{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700121 internal::OffsetList offsets{};
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600122
123 // The offset to VTOC could be 1 or 2 bytes long
124 RecordOffset vtocOffset = vpd.at(offsets::VTOC_PTR);
125 RecordOffset highByte = vpd.at(offsets::VTOC_PTR + 1);
126 vtocOffset |= (highByte << 8);
127
128 // Got the offset to VTOC, skip past record header and keyword header
129 // to get to the record name.
130 auto iterator = vpd.cbegin();
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700131 std::advance(iterator, vtocOffset + sizeof(RecordId) + sizeof(RecordSize) +
132 // Skip past the RT keyword, which contains
133 // the record name.
134 lengths::KW_NAME + sizeof(KwSize));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600135
136 auto stop = std::next(iterator, lengths::RECORD_NAME);
137 std::string record(iterator, stop);
138 if ("VTOC" != record)
139 {
140 throw std::runtime_error("VTOC record not found");
141 }
142
143 // VTOC record name is good, now read through the TOC, stored in the PT
144 // PT keyword; vpdBuffer is now pointing at the first character of the
145 // name 'VTOC', jump to PT data.
146 // Skip past record name and KW name, 'PT'
147 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
148 // Note size of PT
149 std::size_t ptLen = *iterator;
150 // Skip past PT size
151 std::advance(iterator, sizeof(KwSize));
152
153 // vpdBuffer is now pointing to PT data
154 return readPT(iterator, ptLen);
155}
156
157internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
158 std::size_t ptLength) const
159{
160 internal::OffsetList offsets{};
161
162 auto end = iterator;
163 std::advance(end, ptLength);
164
165 // Look at each entry in the PT keyword. In the entry,
166 // we care only about the record offset information.
167 while (iterator < end)
168 {
169 // Skip record name and record type
170 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
171
172 // Get record offset
173 RecordOffset offset = *iterator;
174 RecordOffset highByte = *(iterator + 1);
175 offset |= (highByte << 8);
176 offsets.push_back(offset);
177
178 // Jump record size, record length, ECC offset and ECC length
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700179 std::advance(iterator, sizeof(RecordSize) + sizeof(RecordLength) +
180 sizeof(ECCOffset) + sizeof(ECCLength));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600181 }
182
183 return offsets;
184}
185
Deepak Kodihallia1143462016-11-24 06:28:45 -0600186void Impl::processRecord(std::size_t recordOffset)
187{
188 // Jump to record name
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700189 auto nameOffset = recordOffset + sizeof(RecordId) + sizeof(RecordSize) +
Deepak Kodihallia1143462016-11-24 06:28:45 -0600190 // Skip past the RT keyword, which contains
191 // the record name.
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700192 lengths::KW_NAME + sizeof(KwSize);
Deepak Kodihallia1143462016-11-24 06:28:45 -0600193 // Get record name
194 auto iterator = vpd.cbegin();
195 std::advance(iterator, nameOffset);
196
197 std::string name(iterator, iterator + lengths::RECORD_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500198#ifndef IPZ_PARSER
Deepak Kodihallia1143462016-11-24 06:28:45 -0600199 if (supportedRecords.end() != supportedRecords.find(name))
200 {
Alpana Kumari26a74af2019-09-10 23:53:58 -0500201#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600202 // If it's a record we're interested in, proceed to find
203 // contained keywords and their values.
204 std::advance(iterator, lengths::RECORD_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500205
206#ifdef IPZ_PARSER
207 // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
208 std::advance(iterator, -(lengths::KW_NAME + sizeof(KwSize) +
209 lengths::RECORD_NAME));
210#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600211 auto kwMap = readKeywords(iterator);
212 // Add entry for this record (and contained keyword:value pairs)
213 // to the parsed vpd output.
214 out.emplace(std::move(name), std::move(kwMap));
Alpana Kumari26a74af2019-09-10 23:53:58 -0500215#ifndef IPZ_PARSER
Deepak Kodihallia1143462016-11-24 06:28:45 -0600216 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500217#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600218}
219
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600220std::string Impl::readKwData(const internal::KeywordInfo& keyword,
221 std::size_t dataLength,
222 Binary::const_iterator iterator)
223{
224 switch (std::get<keyword::Encoding>(keyword))
225 {
226 case keyword::Encoding::ASCII:
227 {
228 auto stop = std::next(iterator, dataLength);
229 return std::string(iterator, stop);
230 }
231
232 case keyword::Encoding::RAW:
233 {
234 auto stop = std::next(iterator, dataLength);
235 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700236 std::string result{};
237 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
238 result += toHex(c >> 4);
239 result += toHex(c & 0x0F);
240 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600241 return result;
242 }
243
George Liuee79ca82019-07-12 11:05:33 +0800244 case keyword::Encoding::MB:
245 {
246 // MB is BuildDate, represent as
247 // 1997-01-01-08:30:00
248 // <year>-<month>-<day>-<hour>:<min>:<sec>
249 auto stop = std::next(iterator, MB_LEN_BYTES);
250 std::string data(iterator, stop);
251 std::string result;
252 result.reserve(MB_LEN_BYTES);
253 auto strItr = data.cbegin();
254 std::advance(strItr, 1);
255 std::for_each(strItr, data.cend(), [&result](size_t c) {
256 result += toHex(c >> 4);
257 result += toHex(c & 0x0F);
258 });
259
260 result.insert(MB_YEAR_END, 1, '-');
261 result.insert(MB_MONTH_END, 1, '-');
262 result.insert(MB_DAY_END, 1, '-');
263 result.insert(MB_HOUR_END, 1, ':');
264 result.insert(MB_MIN_END, 1, ':');
265
266 return result;
267 }
268
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600269 case keyword::Encoding::B1:
270 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700271 // B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600272 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
273 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700274 std::string result{};
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600275 auto strItr = data.cbegin();
276 size_t firstDigit = *strItr;
277 result += toHex(firstDigit >> 4);
278 result += toHex(firstDigit & 0x0F);
279 std::advance(strItr, 1);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700280 std::for_each(strItr, data.cend(), [&result](size_t c) {
281 result += ":";
282 result += toHex(c >> 4);
283 result += toHex(c & 0x0F);
284 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600285 return result;
286 }
287
Dinesh Chinaric576b482017-07-17 16:34:10 -0500288 case keyword::Encoding::UD:
289 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700290 // UD, the UUID info, represented as
291 // 123e4567-e89b-12d3-a456-426655440000
Dinesh Chinaric576b482017-07-17 16:34:10 -0500292 //<time_low>-<time_mid>-<time hi and version>
293 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id>
294 auto stop = std::next(iterator, UUID_LEN_BYTES);
295 std::string data(iterator, stop);
296 std::string result{};
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700297 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
298 result += toHex(c >> 4);
299 result += toHex(c & 0x0F);
300 });
Dinesh Chinaric576b482017-07-17 16:34:10 -0500301 result.insert(UUID_TIME_LOW_END, 1, '-');
302 result.insert(UUID_TIME_MID_END, 1, '-');
303 result.insert(UUID_TIME_HIGH_END, 1, '-');
304 result.insert(UUID_CLK_SEQ_END, 1, '-');
305
306 return result;
307 }
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600308 default:
309 break;
310 }
311
312 return {};
313}
314
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600315internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
316{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700317 internal::KeywordMap map{};
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600318 while (true)
319 {
320 // Note keyword name
321 std::string kw(iterator, iterator + lengths::KW_NAME);
322 if (LAST_KW == kw)
323 {
324 // We're done
325 break;
326 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500327 // Check if the Keyword is '#kw'
328 char kwNameStart = *iterator;
329
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600330 // Jump past keyword name
331 std::advance(iterator, lengths::KW_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500332
333 std::size_t length;
334 std::size_t lengthHighByte;
335 if (POUND_KW == kwNameStart)
336 {
337 // Note keyword data length
338 length = *iterator;
339 lengthHighByte = *(iterator + 1);
340 length |= (lengthHighByte << 8);
341
342 // Jump past 2Byte keyword length
343 std::advance(iterator, sizeof(PoundKwSize));
344 }
345 else
346 {
347 // Note keyword data length
348 length = *iterator;
349
350 // Jump past keyword length
351 std::advance(iterator, sizeof(KwSize));
352 }
353
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600354 // Pointing to keyword data now
Alpana Kumari26a74af2019-09-10 23:53:58 -0500355#ifndef IPZ_PARSER
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600356 if (supportedKeywords.end() != supportedKeywords.find(kw))
357 {
358 // Keyword is of interest to us
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700359 std::string data = readKwData((supportedKeywords.find(kw))->second,
360 length, iterator);
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600361 map.emplace(std::move(kw), std::move(data));
362 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500363
364#else
365 // support all the Keywords
366 auto stop = std::next(iterator, length);
367 std::string kwdata(iterator, stop);
368 map.emplace(std::move(kw), std::move(kwdata));
369
370#endif
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600371 // Jump past keyword data length
372 std::advance(iterator, length);
373 }
374
375 return map;
376}
377
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600378Store Impl::run()
379{
380 // Check if the VHDR record is present
381 checkHeader();
382
383 // Read the table of contents record, to get offsets
384 // to other records.
385 auto offsets = readTOC();
386 for (const auto& offset : offsets)
387 {
388 processRecord(offset);
389 }
390
391 // Return a Store object, which has interfaces to
392 // access parsed VPD by record:keyword
393 return Store(std::move(out));
394}
395
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600396} // namespace parser
397} // namespace vpd
398} // namespace openpower