blob: ba85346379514466bb7f0f710aadbc1d0012a149 [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
Patrick Venturec83c4dc2018-11-01 16:29:18 -070032static const std::unordered_map<std::string, internal::KeywordInfo>
33 supportedKeywords = {
34 {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)},
35 {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)},
36 {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)},
37 {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)},
38 {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)},
39 {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)},
40 {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)},
41 {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::RAW)},
42 {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)},
43 {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)},
44 {"VP", std::make_tuple(record::Keyword::VP, keyword::Encoding::ASCII)},
45 {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)},
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060046};
47
Deepak Kodihalli023112f2016-11-22 22:02:14 -060048namespace
49{
50
51using RecordId = uint8_t;
52using RecordOffset = uint16_t;
53using RecordSize = uint16_t;
54using RecordType = uint16_t;
55using RecordLength = uint16_t;
56using KwSize = uint8_t;
57using ECCOffset = uint16_t;
58using ECCLength = uint16_t;
59
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060060constexpr auto toHex(size_t c)
61{
62 constexpr auto map = "0123456789abcdef";
63 return map[c];
64}
65
Patrick Venturec83c4dc2018-11-01 16:29:18 -070066} // namespace
Deepak Kodihalli023112f2016-11-22 22:02:14 -060067
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060068namespace offsets
69{
70
71enum Offsets
72{
Deepak Kodihalli023112f2016-11-22 22:02:14 -060073 VHDR = 17,
74 VHDR_TOC_ENTRY = 29,
75 VTOC_PTR = 35,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060076};
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060077}
78
79namespace lengths
80{
81
82enum Lengths
83{
84 RECORD_NAME = 4,
Deepak Kodihalli023112f2016-11-22 22:02:14 -060085 KW_NAME = 2,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060086 RECORD_MIN = 44,
87};
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060088}
89
90void Impl::checkHeader() const
91{
92 if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
93 {
94 throw std::runtime_error("Malformed VPD");
95 }
96 else
97 {
98 auto iterator = vpd.cbegin();
99 std::advance(iterator, offsets::VHDR);
100 auto stop = std::next(iterator, lengths::RECORD_NAME);
101 std::string record(iterator, stop);
102 if ("VHDR" != record)
103 {
104 throw std::runtime_error("VHDR record not found");
105 }
106 }
107}
108
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600109internal::OffsetList Impl::readTOC() const
110{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700111 internal::OffsetList offsets{};
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600112
113 // The offset to VTOC could be 1 or 2 bytes long
114 RecordOffset vtocOffset = vpd.at(offsets::VTOC_PTR);
115 RecordOffset highByte = vpd.at(offsets::VTOC_PTR + 1);
116 vtocOffset |= (highByte << 8);
117
118 // Got the offset to VTOC, skip past record header and keyword header
119 // to get to the record name.
120 auto iterator = vpd.cbegin();
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700121 std::advance(iterator, vtocOffset + sizeof(RecordId) + sizeof(RecordSize) +
122 // Skip past the RT keyword, which contains
123 // the record name.
124 lengths::KW_NAME + sizeof(KwSize));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600125
126 auto stop = std::next(iterator, lengths::RECORD_NAME);
127 std::string record(iterator, stop);
128 if ("VTOC" != record)
129 {
130 throw std::runtime_error("VTOC record not found");
131 }
132
133 // VTOC record name is good, now read through the TOC, stored in the PT
134 // PT keyword; vpdBuffer is now pointing at the first character of the
135 // name 'VTOC', jump to PT data.
136 // Skip past record name and KW name, 'PT'
137 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
138 // Note size of PT
139 std::size_t ptLen = *iterator;
140 // Skip past PT size
141 std::advance(iterator, sizeof(KwSize));
142
143 // vpdBuffer is now pointing to PT data
144 return readPT(iterator, ptLen);
145}
146
147internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
148 std::size_t ptLength) const
149{
150 internal::OffsetList offsets{};
151
152 auto end = iterator;
153 std::advance(end, ptLength);
154
155 // Look at each entry in the PT keyword. In the entry,
156 // we care only about the record offset information.
157 while (iterator < end)
158 {
159 // Skip record name and record type
160 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
161
162 // Get record offset
163 RecordOffset offset = *iterator;
164 RecordOffset highByte = *(iterator + 1);
165 offset |= (highByte << 8);
166 offsets.push_back(offset);
167
168 // Jump record size, record length, ECC offset and ECC length
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700169 std::advance(iterator, sizeof(RecordSize) + sizeof(RecordLength) +
170 sizeof(ECCOffset) + sizeof(ECCLength));
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600171 }
172
173 return offsets;
174}
175
Deepak Kodihallia1143462016-11-24 06:28:45 -0600176void Impl::processRecord(std::size_t recordOffset)
177{
178 // Jump to record name
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700179 auto nameOffset = recordOffset + sizeof(RecordId) + sizeof(RecordSize) +
Deepak Kodihallia1143462016-11-24 06:28:45 -0600180 // Skip past the RT keyword, which contains
181 // the record name.
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700182 lengths::KW_NAME + sizeof(KwSize);
Deepak Kodihallia1143462016-11-24 06:28:45 -0600183 // Get record name
184 auto iterator = vpd.cbegin();
185 std::advance(iterator, nameOffset);
186
187 std::string name(iterator, iterator + lengths::RECORD_NAME);
188 if (supportedRecords.end() != supportedRecords.find(name))
189 {
190 // If it's a record we're interested in, proceed to find
191 // contained keywords and their values.
192 std::advance(iterator, lengths::RECORD_NAME);
193 auto kwMap = readKeywords(iterator);
194 // Add entry for this record (and contained keyword:value pairs)
195 // to the parsed vpd output.
196 out.emplace(std::move(name), std::move(kwMap));
197 }
198}
199
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600200std::string Impl::readKwData(const internal::KeywordInfo& keyword,
201 std::size_t dataLength,
202 Binary::const_iterator iterator)
203{
204 switch (std::get<keyword::Encoding>(keyword))
205 {
206 case keyword::Encoding::ASCII:
207 {
208 auto stop = std::next(iterator, dataLength);
209 return std::string(iterator, stop);
210 }
211
212 case keyword::Encoding::RAW:
213 {
214 auto stop = std::next(iterator, dataLength);
215 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700216 std::string result{};
217 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
218 result += toHex(c >> 4);
219 result += toHex(c & 0x0F);
220 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600221 return result;
222 }
223
224 case keyword::Encoding::B1:
225 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700226 // B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600227 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
228 std::string data(iterator, stop);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700229 std::string result{};
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600230 auto strItr = data.cbegin();
231 size_t firstDigit = *strItr;
232 result += toHex(firstDigit >> 4);
233 result += toHex(firstDigit & 0x0F);
234 std::advance(strItr, 1);
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700235 std::for_each(strItr, data.cend(), [&result](size_t c) {
236 result += ":";
237 result += toHex(c >> 4);
238 result += toHex(c & 0x0F);
239 });
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600240 return result;
241 }
242
Dinesh Chinaric576b482017-07-17 16:34:10 -0500243 case keyword::Encoding::UD:
244 {
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700245 // UD, the UUID info, represented as
246 // 123e4567-e89b-12d3-a456-426655440000
Dinesh Chinaric576b482017-07-17 16:34:10 -0500247 //<time_low>-<time_mid>-<time hi and version>
248 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id>
249 auto stop = std::next(iterator, UUID_LEN_BYTES);
250 std::string data(iterator, stop);
251 std::string result{};
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700252 std::for_each(data.cbegin(), data.cend(), [&result](size_t c) {
253 result += toHex(c >> 4);
254 result += toHex(c & 0x0F);
255 });
Dinesh Chinaric576b482017-07-17 16:34:10 -0500256 result.insert(UUID_TIME_LOW_END, 1, '-');
257 result.insert(UUID_TIME_MID_END, 1, '-');
258 result.insert(UUID_TIME_HIGH_END, 1, '-');
259 result.insert(UUID_CLK_SEQ_END, 1, '-');
260
261 return result;
262 }
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600263 default:
264 break;
265 }
266
267 return {};
268}
269
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600270internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
271{
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700272 internal::KeywordMap map{};
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600273 while (true)
274 {
275 // Note keyword name
276 std::string kw(iterator, iterator + lengths::KW_NAME);
277 if (LAST_KW == kw)
278 {
279 // We're done
280 break;
281 }
282 // Jump past keyword name
283 std::advance(iterator, lengths::KW_NAME);
284 // Note keyword data length
285 std::size_t length = *iterator;
286 // Jump past keyword length
287 std::advance(iterator, sizeof(KwSize));
288 // Pointing to keyword data now
289 if (supportedKeywords.end() != supportedKeywords.find(kw))
290 {
291 // Keyword is of interest to us
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700292 std::string data = readKwData((supportedKeywords.find(kw))->second,
293 length, iterator);
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600294 map.emplace(std::move(kw), std::move(data));
295 }
296 // Jump past keyword data length
297 std::advance(iterator, length);
298 }
299
300 return map;
301}
302
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600303Store Impl::run()
304{
305 // Check if the VHDR record is present
306 checkHeader();
307
308 // Read the table of contents record, to get offsets
309 // to other records.
310 auto offsets = readTOC();
311 for (const auto& offset : offsets)
312 {
313 processRecord(offset);
314 }
315
316 // Return a Store object, which has interfaces to
317 // access parsed VPD by record:keyword
318 return Store(std::move(out));
319}
320
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600321} // namespace parser
322} // namespace vpd
323} // namespace openpower