blob: 25da01f9d4f1dde013066a16746dc556191d01ad [file] [log] [blame]
Patrick Venturec83c4dc2018-11-01 16:29:18 -07001#include "impl.hpp"
2
3#include "defines.hpp"
Santosh Puranikbd011b22020-01-23 04:05:25 -06004#include "utils.hpp"
Patrick Venturec83c4dc2018-11-01 16:29:18 -07005
6#include <algorithm>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -06007#include <exception>
Patrick Venturec83c4dc2018-11-01 16:29:18 -07008#include <iomanip>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -06009#include <iostream>
10#include <iterator>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070011#include <sstream>
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060012#include <tuple>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070013#include <unordered_map>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060014
15namespace openpower
16{
17namespace vpd
18{
19namespace parser
20{
21
Patrick Venturec83c4dc2018-11-01 16:29:18 -070022static const std::unordered_map<std::string, Record> supportedRecords = {
23 {"VINI", Record::VINI}, {"OPFR", Record::OPFR}, {"OSYS", Record::OSYS}};
Deepak Kodihallia1143462016-11-24 06:28:45 -060024
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060025static constexpr auto MAC_ADDRESS_LEN_BYTES = 6;
Deepak Kodihalli683bf722016-11-24 06:50:43 -060026static constexpr auto LAST_KW = "PF";
Alpana Kumari26a74af2019-09-10 23:53:58 -050027static constexpr auto POUND_KW = '#';
Dinesh Chinaric576b482017-07-17 16:34:10 -050028static constexpr auto UUID_LEN_BYTES = 16;
29static constexpr auto UUID_TIME_LOW_END = 8;
30static constexpr auto UUID_TIME_MID_END = 13;
31static constexpr auto UUID_TIME_HIGH_END = 18;
32static constexpr auto UUID_CLK_SEQ_END = 23;
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060033
George Liuee79ca82019-07-12 11:05:33 +080034static constexpr auto MB_RESULT_LEN = 19;
35static constexpr auto MB_LEN_BYTES = 8;
36static constexpr auto MB_YEAR_END = 4;
37static constexpr auto MB_MONTH_END = 7;
38static constexpr auto MB_DAY_END = 10;
39static constexpr auto MB_HOUR_END = 13;
40static constexpr auto MB_MIN_END = 16;
41
Patrick Venturec83c4dc2018-11-01 16:29:18 -070042static const std::unordered_map<std::string, internal::KeywordInfo>
43 supportedKeywords = {
44 {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)},
45 {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)},
46 {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)},
47 {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)},
48 {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)},
49 {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)},
50 {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)},
George Liuee79ca82019-07-12 11:05:33 +080051 {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::MB)},
Patrick Venturec83c4dc2018-11-01 16:29:18 -070052 {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)},
53 {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)},
54 {"VP", std::make_tuple(record::Keyword::VP, keyword::Encoding::ASCII)},
55 {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)},
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060056};
57
Deepak Kodihalli023112f2016-11-22 22:02:14 -060058namespace
59{
60
61using RecordId = uint8_t;
62using RecordOffset = uint16_t;
63using RecordSize = uint16_t;
64using RecordType = uint16_t;
65using RecordLength = uint16_t;
66using KwSize = uint8_t;
Alpana Kumari26a74af2019-09-10 23:53:58 -050067using PoundKwSize = uint16_t;
Deepak Kodihalli023112f2016-11-22 22:02:14 -060068using ECCOffset = uint16_t;
69using ECCLength = uint16_t;
70
Patrick Venturec83c4dc2018-11-01 16:29:18 -070071} // namespace
Deepak Kodihalli023112f2016-11-22 22:02:14 -060072
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060073namespace offsets
74{
75
76enum Offsets
77{
Deepak Kodihalli023112f2016-11-22 22:02:14 -060078 VHDR = 17,
79 VHDR_TOC_ENTRY = 29,
80 VTOC_PTR = 35,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060081};
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060082}
83
84namespace lengths
85{
86
87enum Lengths
88{
89 RECORD_NAME = 4,
Deepak Kodihalli023112f2016-11-22 22:02:14 -060090 KW_NAME = 2,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060091 RECORD_MIN = 44,
92};
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060093}
94
95void Impl::checkHeader() const
96{
97 if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
98 {
99 throw std::runtime_error("Malformed VPD");
100 }
101 else
102 {
103 auto iterator = vpd.cbegin();
104 std::advance(iterator, offsets::VHDR);
105 auto stop = std::next(iterator, lengths::RECORD_NAME);
106 std::string record(iterator, stop);
107 if ("VHDR" != record)
108 {
109 throw std::runtime_error("VHDR record not found");
110 }
111 }
112}
113
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600114internal::OffsetList Impl::readTOC() const
115{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700116 internal::OffsetList offsets{};
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600117
118 // The offset to VTOC could be 1 or 2 bytes long
119 RecordOffset vtocOffset = vpd.at(offsets::VTOC_PTR);
120 RecordOffset highByte = vpd.at(offsets::VTOC_PTR + 1);
121 vtocOffset |= (highByte << 8);
122
123 // Got the offset to VTOC, skip past record header and keyword header
124 // to get to the record name.
125 auto iterator = vpd.cbegin();
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700126 std::advance(iterator, vtocOffset + sizeof(RecordId) + sizeof(RecordSize) +
127 // Skip past the RT keyword, which contains
128 // the record name.
129 lengths::KW_NAME + sizeof(KwSize));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600130
131 auto stop = std::next(iterator, lengths::RECORD_NAME);
132 std::string record(iterator, stop);
133 if ("VTOC" != record)
134 {
135 throw std::runtime_error("VTOC record not found");
136 }
137
138 // VTOC record name is good, now read through the TOC, stored in the PT
139 // PT keyword; vpdBuffer is now pointing at the first character of the
140 // name 'VTOC', jump to PT data.
141 // Skip past record name and KW name, 'PT'
142 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
143 // Note size of PT
144 std::size_t ptLen = *iterator;
145 // Skip past PT size
146 std::advance(iterator, sizeof(KwSize));
147
148 // vpdBuffer is now pointing to PT data
149 return readPT(iterator, ptLen);
150}
151
152internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
153 std::size_t ptLength) const
154{
155 internal::OffsetList offsets{};
156
157 auto end = iterator;
158 std::advance(end, ptLength);
159
160 // Look at each entry in the PT keyword. In the entry,
161 // we care only about the record offset information.
162 while (iterator < end)
163 {
164 // Skip record name and record type
165 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
166
167 // Get record offset
168 RecordOffset offset = *iterator;
169 RecordOffset highByte = *(iterator + 1);
170 offset |= (highByte << 8);
171 offsets.push_back(offset);
172
173 // Jump record size, record length, ECC offset and ECC length
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700174 std::advance(iterator, sizeof(RecordSize) + sizeof(RecordLength) +
175 sizeof(ECCOffset) + sizeof(ECCLength));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600176 }
177
178 return offsets;
179}
180
Deepak Kodihallia1143462016-11-24 06:28:45 -0600181void Impl::processRecord(std::size_t recordOffset)
182{
183 // Jump to record name
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700184 auto nameOffset = recordOffset + sizeof(RecordId) + sizeof(RecordSize) +
Deepak Kodihallia1143462016-11-24 06:28:45 -0600185 // Skip past the RT keyword, which contains
186 // the record name.
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700187 lengths::KW_NAME + sizeof(KwSize);
Deepak Kodihallia1143462016-11-24 06:28:45 -0600188 // Get record name
189 auto iterator = vpd.cbegin();
190 std::advance(iterator, nameOffset);
191
192 std::string name(iterator, iterator + lengths::RECORD_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500193#ifndef IPZ_PARSER
Deepak Kodihallia1143462016-11-24 06:28:45 -0600194 if (supportedRecords.end() != supportedRecords.find(name))
195 {
Alpana Kumari26a74af2019-09-10 23:53:58 -0500196#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600197 // If it's a record we're interested in, proceed to find
198 // contained keywords and their values.
199 std::advance(iterator, lengths::RECORD_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500200
201#ifdef IPZ_PARSER
202 // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
203 std::advance(iterator, -(lengths::KW_NAME + sizeof(KwSize) +
204 lengths::RECORD_NAME));
205#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600206 auto kwMap = readKeywords(iterator);
207 // Add entry for this record (and contained keyword:value pairs)
208 // to the parsed vpd output.
209 out.emplace(std::move(name), std::move(kwMap));
Alpana Kumari26a74af2019-09-10 23:53:58 -0500210#ifndef IPZ_PARSER
Deepak Kodihallia1143462016-11-24 06:28:45 -0600211 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500212#endif
Deepak Kodihallia1143462016-11-24 06:28:45 -0600213}
214
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600215std::string Impl::readKwData(const internal::KeywordInfo& keyword,
216 std::size_t dataLength,
217 Binary::const_iterator iterator)
218{
Santosh Puranikbd011b22020-01-23 04:05:25 -0600219 using namespace openpower::vpd;
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600220 switch (std::get<keyword::Encoding>(keyword))
221 {
222 case keyword::Encoding::ASCII:
223 {
224 auto stop = std::next(iterator, dataLength);
225 return std::string(iterator, stop);
226 }
227
228 case keyword::Encoding::RAW:
229 {
230 auto stop = std::next(iterator, dataLength);
231 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700232 std::string result{};
233 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
234 result += toHex(c >> 4);
235 result += toHex(c & 0x0F);
236 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600237 return result;
238 }
239
George Liuee79ca82019-07-12 11:05:33 +0800240 case keyword::Encoding::MB:
241 {
242 // MB is BuildDate, represent as
243 // 1997-01-01-08:30:00
244 // <year>-<month>-<day>-<hour>:<min>:<sec>
245 auto stop = std::next(iterator, MB_LEN_BYTES);
246 std::string data(iterator, stop);
247 std::string result;
248 result.reserve(MB_LEN_BYTES);
249 auto strItr = data.cbegin();
250 std::advance(strItr, 1);
251 std::for_each(strItr, data.cend(), [&result](size_t c) {
252 result += toHex(c >> 4);
253 result += toHex(c & 0x0F);
254 });
255
256 result.insert(MB_YEAR_END, 1, '-');
257 result.insert(MB_MONTH_END, 1, '-');
258 result.insert(MB_DAY_END, 1, '-');
259 result.insert(MB_HOUR_END, 1, ':');
260 result.insert(MB_MIN_END, 1, ':');
261
262 return result;
263 }
264
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600265 case keyword::Encoding::B1:
266 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700267 // B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600268 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
269 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700270 std::string result{};
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600271 auto strItr = data.cbegin();
272 size_t firstDigit = *strItr;
273 result += toHex(firstDigit >> 4);
274 result += toHex(firstDigit & 0x0F);
275 std::advance(strItr, 1);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700276 std::for_each(strItr, data.cend(), [&result](size_t c) {
277 result += ":";
278 result += toHex(c >> 4);
279 result += toHex(c & 0x0F);
280 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600281 return result;
282 }
283
Dinesh Chinaric576b482017-07-17 16:34:10 -0500284 case keyword::Encoding::UD:
285 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700286 // UD, the UUID info, represented as
287 // 123e4567-e89b-12d3-a456-426655440000
Dinesh Chinaric576b482017-07-17 16:34:10 -0500288 //<time_low>-<time_mid>-<time hi and version>
289 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id>
290 auto stop = std::next(iterator, UUID_LEN_BYTES);
291 std::string data(iterator, stop);
292 std::string result{};
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700293 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
294 result += toHex(c >> 4);
295 result += toHex(c & 0x0F);
296 });
Dinesh Chinaric576b482017-07-17 16:34:10 -0500297 result.insert(UUID_TIME_LOW_END, 1, '-');
298 result.insert(UUID_TIME_MID_END, 1, '-');
299 result.insert(UUID_TIME_HIGH_END, 1, '-');
300 result.insert(UUID_CLK_SEQ_END, 1, '-');
301
302 return result;
303 }
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600304 default:
305 break;
306 }
307
308 return {};
309}
310
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600311internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
312{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700313 internal::KeywordMap map{};
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600314 while (true)
315 {
316 // Note keyword name
317 std::string kw(iterator, iterator + lengths::KW_NAME);
318 if (LAST_KW == kw)
319 {
320 // We're done
321 break;
322 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500323 // Check if the Keyword is '#kw'
324 char kwNameStart = *iterator;
325
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600326 // Jump past keyword name
327 std::advance(iterator, lengths::KW_NAME);
Alpana Kumari26a74af2019-09-10 23:53:58 -0500328
329 std::size_t length;
330 std::size_t lengthHighByte;
331 if (POUND_KW == kwNameStart)
332 {
333 // Note keyword data length
334 length = *iterator;
335 lengthHighByte = *(iterator + 1);
336 length |= (lengthHighByte << 8);
337
338 // Jump past 2Byte keyword length
339 std::advance(iterator, sizeof(PoundKwSize));
340 }
341 else
342 {
343 // Note keyword data length
344 length = *iterator;
345
346 // Jump past keyword length
347 std::advance(iterator, sizeof(KwSize));
348 }
349
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600350 // Pointing to keyword data now
Alpana Kumari26a74af2019-09-10 23:53:58 -0500351#ifndef IPZ_PARSER
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600352 if (supportedKeywords.end() != supportedKeywords.find(kw))
353 {
354 // Keyword is of interest to us
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700355 std::string data = readKwData((supportedKeywords.find(kw))->second,
356 length, iterator);
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600357 map.emplace(std::move(kw), std::move(data));
358 }
Alpana Kumari26a74af2019-09-10 23:53:58 -0500359
360#else
361 // support all the Keywords
362 auto stop = std::next(iterator, length);
363 std::string kwdata(iterator, stop);
364 map.emplace(std::move(kw), std::move(kwdata));
365
366#endif
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600367 // Jump past keyword data length
368 std::advance(iterator, length);
369 }
370
371 return map;
372}
373
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600374Store Impl::run()
375{
376 // Check if the VHDR record is present
377 checkHeader();
378
379 // Read the table of contents record, to get offsets
380 // to other records.
381 auto offsets = readTOC();
382 for (const auto& offset : offsets)
383 {
384 processRecord(offset);
385 }
386
387 // Return a Store object, which has interfaces to
388 // access parsed VPD by record:keyword
389 return Store(std::move(out));
390}
391
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600392} // namespace parser
393} // namespace vpd
394} // namespace openpower