blob: 765b1ac7ebd2628927bc804465170dc62af72ef7 [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";
Dinesh Chinaric576b482017-07-17 16:34:10 -050026static constexpr auto UUID_LEN_BYTES = 16;
27static constexpr auto UUID_TIME_LOW_END = 8;
28static constexpr auto UUID_TIME_MID_END = 13;
29static constexpr auto UUID_TIME_HIGH_END = 18;
30static constexpr auto UUID_CLK_SEQ_END = 23;
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060031
George Liuee79ca82019-07-12 11:05:33 +080032static constexpr auto MB_RESULT_LEN = 19;
33static constexpr auto MB_LEN_BYTES = 8;
34static constexpr auto MB_YEAR_END = 4;
35static constexpr auto MB_MONTH_END = 7;
36static constexpr auto MB_DAY_END = 10;
37static constexpr auto MB_HOUR_END = 13;
38static constexpr auto MB_MIN_END = 16;
39
Patrick Venturec83c4dc2018-11-01 16:29:18 -070040static const std::unordered_map<std::string, internal::KeywordInfo>
41 supportedKeywords = {
42 {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)},
43 {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)},
44 {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)},
45 {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)},
46 {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)},
47 {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)},
48 {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)},
George Liuee79ca82019-07-12 11:05:33 +080049 {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::MB)},
Patrick Venturec83c4dc2018-11-01 16:29:18 -070050 {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)},
51 {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)},
52 {"VP", std::make_tuple(record::Keyword::VP, keyword::Encoding::ASCII)},
53 {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)},
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060054};
55
Deepak Kodihalli023112f2016-11-22 22:02:14 -060056namespace
57{
58
59using RecordId = uint8_t;
60using RecordOffset = uint16_t;
61using RecordSize = uint16_t;
62using RecordType = uint16_t;
63using RecordLength = uint16_t;
64using KwSize = uint8_t;
65using ECCOffset = uint16_t;
66using ECCLength = uint16_t;
67
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060068constexpr auto toHex(size_t c)
69{
70 constexpr auto map = "0123456789abcdef";
71 return map[c];
72}
73
Patrick Venturec83c4dc2018-11-01 16:29:18 -070074} // namespace
Deepak Kodihalli023112f2016-11-22 22:02:14 -060075
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060076namespace offsets
77{
78
79enum Offsets
80{
Deepak Kodihalli023112f2016-11-22 22:02:14 -060081 VHDR = 17,
82 VHDR_TOC_ENTRY = 29,
83 VTOC_PTR = 35,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060084};
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060085}
86
87namespace lengths
88{
89
90enum Lengths
91{
92 RECORD_NAME = 4,
Deepak Kodihalli023112f2016-11-22 22:02:14 -060093 KW_NAME = 2,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060094 RECORD_MIN = 44,
95};
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060096}
97
98void Impl::checkHeader() const
99{
100 if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
101 {
102 throw std::runtime_error("Malformed VPD");
103 }
104 else
105 {
106 auto iterator = vpd.cbegin();
107 std::advance(iterator, offsets::VHDR);
108 auto stop = std::next(iterator, lengths::RECORD_NAME);
109 std::string record(iterator, stop);
110 if ("VHDR" != record)
111 {
112 throw std::runtime_error("VHDR record not found");
113 }
114 }
115}
116
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600117internal::OffsetList Impl::readTOC() const
118{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700119 internal::OffsetList offsets{};
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600120
121 // The offset to VTOC could be 1 or 2 bytes long
122 RecordOffset vtocOffset = vpd.at(offsets::VTOC_PTR);
123 RecordOffset highByte = vpd.at(offsets::VTOC_PTR + 1);
124 vtocOffset |= (highByte << 8);
125
126 // Got the offset to VTOC, skip past record header and keyword header
127 // to get to the record name.
128 auto iterator = vpd.cbegin();
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700129 std::advance(iterator, vtocOffset + sizeof(RecordId) + sizeof(RecordSize) +
130 // Skip past the RT keyword, which contains
131 // the record name.
132 lengths::KW_NAME + sizeof(KwSize));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600133
134 auto stop = std::next(iterator, lengths::RECORD_NAME);
135 std::string record(iterator, stop);
136 if ("VTOC" != record)
137 {
138 throw std::runtime_error("VTOC record not found");
139 }
140
141 // VTOC record name is good, now read through the TOC, stored in the PT
142 // PT keyword; vpdBuffer is now pointing at the first character of the
143 // name 'VTOC', jump to PT data.
144 // Skip past record name and KW name, 'PT'
145 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
146 // Note size of PT
147 std::size_t ptLen = *iterator;
148 // Skip past PT size
149 std::advance(iterator, sizeof(KwSize));
150
151 // vpdBuffer is now pointing to PT data
152 return readPT(iterator, ptLen);
153}
154
155internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
156 std::size_t ptLength) const
157{
158 internal::OffsetList offsets{};
159
160 auto end = iterator;
161 std::advance(end, ptLength);
162
163 // Look at each entry in the PT keyword. In the entry,
164 // we care only about the record offset information.
165 while (iterator < end)
166 {
167 // Skip record name and record type
168 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
169
170 // Get record offset
171 RecordOffset offset = *iterator;
172 RecordOffset highByte = *(iterator + 1);
173 offset |= (highByte << 8);
174 offsets.push_back(offset);
175
176 // Jump record size, record length, ECC offset and ECC length
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700177 std::advance(iterator, sizeof(RecordSize) + sizeof(RecordLength) +
178 sizeof(ECCOffset) + sizeof(ECCLength));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600179 }
180
181 return offsets;
182}
183
Deepak Kodihallia1143462016-11-24 06:28:45 -0600184void Impl::processRecord(std::size_t recordOffset)
185{
186 // Jump to record name
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700187 auto nameOffset = recordOffset + sizeof(RecordId) + sizeof(RecordSize) +
Deepak Kodihallia1143462016-11-24 06:28:45 -0600188 // Skip past the RT keyword, which contains
189 // the record name.
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700190 lengths::KW_NAME + sizeof(KwSize);
Deepak Kodihallia1143462016-11-24 06:28:45 -0600191 // Get record name
192 auto iterator = vpd.cbegin();
193 std::advance(iterator, nameOffset);
194
195 std::string name(iterator, iterator + lengths::RECORD_NAME);
196 if (supportedRecords.end() != supportedRecords.find(name))
197 {
198 // If it's a record we're interested in, proceed to find
199 // contained keywords and their values.
200 std::advance(iterator, lengths::RECORD_NAME);
201 auto kwMap = readKeywords(iterator);
202 // Add entry for this record (and contained keyword:value pairs)
203 // to the parsed vpd output.
204 out.emplace(std::move(name), std::move(kwMap));
205 }
206}
207
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600208std::string Impl::readKwData(const internal::KeywordInfo& keyword,
209 std::size_t dataLength,
210 Binary::const_iterator iterator)
211{
212 switch (std::get<keyword::Encoding>(keyword))
213 {
214 case keyword::Encoding::ASCII:
215 {
216 auto stop = std::next(iterator, dataLength);
217 return std::string(iterator, stop);
218 }
219
220 case keyword::Encoding::RAW:
221 {
222 auto stop = std::next(iterator, dataLength);
223 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700224 std::string result{};
225 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
226 result += toHex(c >> 4);
227 result += toHex(c & 0x0F);
228 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600229 return result;
230 }
231
George Liuee79ca82019-07-12 11:05:33 +0800232 case keyword::Encoding::MB:
233 {
234 // MB is BuildDate, represent as
235 // 1997-01-01-08:30:00
236 // <year>-<month>-<day>-<hour>:<min>:<sec>
237 auto stop = std::next(iterator, MB_LEN_BYTES);
238 std::string data(iterator, stop);
239 std::string result;
240 result.reserve(MB_LEN_BYTES);
241 auto strItr = data.cbegin();
242 std::advance(strItr, 1);
243 std::for_each(strItr, data.cend(), [&result](size_t c) {
244 result += toHex(c >> 4);
245 result += toHex(c & 0x0F);
246 });
247
248 result.insert(MB_YEAR_END, 1, '-');
249 result.insert(MB_MONTH_END, 1, '-');
250 result.insert(MB_DAY_END, 1, '-');
251 result.insert(MB_HOUR_END, 1, ':');
252 result.insert(MB_MIN_END, 1, ':');
253
254 return result;
255 }
256
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600257 case keyword::Encoding::B1:
258 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700259 // B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600260 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
261 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700262 std::string result{};
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600263 auto strItr = data.cbegin();
264 size_t firstDigit = *strItr;
265 result += toHex(firstDigit >> 4);
266 result += toHex(firstDigit & 0x0F);
267 std::advance(strItr, 1);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700268 std::for_each(strItr, data.cend(), [&result](size_t c) {
269 result += ":";
270 result += toHex(c >> 4);
271 result += toHex(c & 0x0F);
272 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600273 return result;
274 }
275
Dinesh Chinaric576b482017-07-17 16:34:10 -0500276 case keyword::Encoding::UD:
277 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700278 // UD, the UUID info, represented as
279 // 123e4567-e89b-12d3-a456-426655440000
Dinesh Chinaric576b482017-07-17 16:34:10 -0500280 //<time_low>-<time_mid>-<time hi and version>
281 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id>
282 auto stop = std::next(iterator, UUID_LEN_BYTES);
283 std::string data(iterator, stop);
284 std::string result{};
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700285 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
286 result += toHex(c >> 4);
287 result += toHex(c & 0x0F);
288 });
Dinesh Chinaric576b482017-07-17 16:34:10 -0500289 result.insert(UUID_TIME_LOW_END, 1, '-');
290 result.insert(UUID_TIME_MID_END, 1, '-');
291 result.insert(UUID_TIME_HIGH_END, 1, '-');
292 result.insert(UUID_CLK_SEQ_END, 1, '-');
293
294 return result;
295 }
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600296 default:
297 break;
298 }
299
300 return {};
301}
302
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600303internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
304{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700305 internal::KeywordMap map{};
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600306 while (true)
307 {
308 // Note keyword name
309 std::string kw(iterator, iterator + lengths::KW_NAME);
310 if (LAST_KW == kw)
311 {
312 // We're done
313 break;
314 }
315 // Jump past keyword name
316 std::advance(iterator, lengths::KW_NAME);
317 // Note keyword data length
318 std::size_t length = *iterator;
319 // Jump past keyword length
320 std::advance(iterator, sizeof(KwSize));
321 // Pointing to keyword data now
322 if (supportedKeywords.end() != supportedKeywords.find(kw))
323 {
324 // Keyword is of interest to us
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700325 std::string data = readKwData((supportedKeywords.find(kw))->second,
326 length, iterator);
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600327 map.emplace(std::move(kw), std::move(data));
328 }
329 // Jump past keyword data length
330 std::advance(iterator, length);
331 }
332
333 return map;
334}
335
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600336Store Impl::run()
337{
338 // Check if the VHDR record is present
339 checkHeader();
340
341 // Read the table of contents record, to get offsets
342 // to other records.
343 auto offsets = readTOC();
344 for (const auto& offset : offsets)
345 {
346 processRecord(offset);
347 }
348
349 // Return a Store object, which has interfaces to
350 // access parsed VPD by record:keyword
351 return Store(std::move(out));
352}
353
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600354} // namespace parser
355} // namespace vpd
356} // namespace openpower