blob: 925b693c67d67c6835c5fe1187fca028e6bf544d [file] [log] [blame]
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -06001#include <sstream>
Deepak Kodihalli810c9de2016-11-22 11:42:51 -06002#include <exception>
3#include <iostream>
4#include <iterator>
Deepak Kodihalli683bf722016-11-24 06:50:43 -06005#include <unordered_map>
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -06006#include <iomanip>
7#include <tuple>
8#include <algorithm>
9#include "defines.hpp"
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060010#include "impl.hpp"
11
12namespace openpower
13{
14namespace vpd
15{
16namespace parser
17{
18
Deepak Kodihallia1143462016-11-24 06:28:45 -060019static const std::unordered_map<std::string, Record> supportedRecords =
20{
21 {"VINI", Record::VINI},
22 {"OPFR", Record::OPFR},
23 {"OSYS", Record::OSYS}
24};
25
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060026static constexpr auto MAC_ADDRESS_LEN_BYTES = 6;
Deepak Kodihalli683bf722016-11-24 06:50:43 -060027static constexpr auto LAST_KW = "PF";
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
34static const std::unordered_map<std::string,
35 internal::KeywordInfo> supportedKeywords =
36{
37 {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)},
38 {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)},
39 {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)},
40 {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)},
41 {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)},
42 {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)},
43 {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)},
44 {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::RAW)},
Dinesh Chinaric576b482017-07-17 16:34:10 -050045 {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)},
Matt Spinlercfbb1ff2018-05-10 10:43:03 -050046 {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)},
47 {"VP", std::make_tuple(record::Keyword::VP, keyword::Encoding::ASCII)},
48 {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)},
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060049};
50
Deepak Kodihalli023112f2016-11-22 22:02:14 -060051namespace
52{
53
54using RecordId = uint8_t;
55using RecordOffset = uint16_t;
56using RecordSize = uint16_t;
57using RecordType = uint16_t;
58using RecordLength = uint16_t;
59using KwSize = uint8_t;
60using ECCOffset = uint16_t;
61using ECCLength = uint16_t;
62
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060063constexpr auto toHex(size_t c)
64{
65 constexpr auto map = "0123456789abcdef";
66 return map[c];
67}
68
Deepak Kodihalli023112f2016-11-22 22:02:14 -060069}
70
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060071namespace offsets
72{
73
74enum Offsets
75{
Deepak Kodihalli023112f2016-11-22 22:02:14 -060076 VHDR = 17,
77 VHDR_TOC_ENTRY = 29,
78 VTOC_PTR = 35,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060079};
80
81}
82
83namespace lengths
84{
85
86enum Lengths
87{
88 RECORD_NAME = 4,
Deepak Kodihalli023112f2016-11-22 22:02:14 -060089 KW_NAME = 2,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060090 RECORD_MIN = 44,
91};
92
93}
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{
116 internal::OffsetList offsets {};
117
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();
126 std::advance(iterator,
127 vtocOffset +
128 sizeof(RecordId) +
129 sizeof(RecordSize) +
130 // Skip past the RT keyword, which contains
131 // the record name.
132 lengths::KW_NAME +
133 sizeof(KwSize));
134
135 auto stop = std::next(iterator, lengths::RECORD_NAME);
136 std::string record(iterator, stop);
137 if ("VTOC" != record)
138 {
139 throw std::runtime_error("VTOC record not found");
140 }
141
142 // VTOC record name is good, now read through the TOC, stored in the PT
143 // PT keyword; vpdBuffer is now pointing at the first character of the
144 // name 'VTOC', jump to PT data.
145 // Skip past record name and KW name, 'PT'
146 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
147 // Note size of PT
148 std::size_t ptLen = *iterator;
149 // Skip past PT size
150 std::advance(iterator, sizeof(KwSize));
151
152 // vpdBuffer is now pointing to PT data
153 return readPT(iterator, ptLen);
154}
155
156internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
157 std::size_t ptLength) const
158{
159 internal::OffsetList offsets{};
160
161 auto end = iterator;
162 std::advance(end, ptLength);
163
164 // Look at each entry in the PT keyword. In the entry,
165 // we care only about the record offset information.
166 while (iterator < end)
167 {
168 // Skip record name and record type
169 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
170
171 // Get record offset
172 RecordOffset offset = *iterator;
173 RecordOffset highByte = *(iterator + 1);
174 offset |= (highByte << 8);
175 offsets.push_back(offset);
176
177 // Jump record size, record length, ECC offset and ECC length
178 std::advance(iterator,
179 sizeof(RecordSize) +
180 sizeof(RecordLength) +
181 sizeof(ECCOffset) +
182 sizeof(ECCLength));
183 }
184
185 return offsets;
186}
187
Deepak Kodihallia1143462016-11-24 06:28:45 -0600188void Impl::processRecord(std::size_t recordOffset)
189{
190 // Jump to record name
191 auto nameOffset = recordOffset +
192 sizeof(RecordId) +
193 sizeof(RecordSize) +
194 // Skip past the RT keyword, which contains
195 // the record name.
196 lengths::KW_NAME +
197 sizeof(KwSize);
198 // Get record name
199 auto iterator = vpd.cbegin();
200 std::advance(iterator, nameOffset);
201
202 std::string name(iterator, iterator + lengths::RECORD_NAME);
203 if (supportedRecords.end() != supportedRecords.find(name))
204 {
205 // If it's a record we're interested in, proceed to find
206 // contained keywords and their values.
207 std::advance(iterator, lengths::RECORD_NAME);
208 auto kwMap = readKeywords(iterator);
209 // Add entry for this record (and contained keyword:value pairs)
210 // to the parsed vpd output.
211 out.emplace(std::move(name), std::move(kwMap));
212 }
213}
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{
219 switch (std::get<keyword::Encoding>(keyword))
220 {
221 case keyword::Encoding::ASCII:
222 {
223 auto stop = std::next(iterator, dataLength);
224 return std::string(iterator, stop);
225 }
226
227 case keyword::Encoding::RAW:
228 {
229 auto stop = std::next(iterator, dataLength);
230 std::string data(iterator, stop);
231 std::string result {};
232 std::for_each(data.cbegin(), data.cend(),
233 [&result](size_t c)
234 {
235 result += toHex(c >> 4);
236 result += toHex(c & 0x0F);
237 });
238 return result;
239 }
240
241 case keyword::Encoding::B1:
242 {
243 //B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
244 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
245 std::string data(iterator, stop);
246 std::string result {};
247 auto strItr = data.cbegin();
248 size_t firstDigit = *strItr;
249 result += toHex(firstDigit >> 4);
250 result += toHex(firstDigit & 0x0F);
251 std::advance(strItr, 1);
252 std::for_each(strItr, data.cend(),
253 [&result](size_t c)
254 {
255 result += ":";
256 result += toHex(c >> 4);
257 result += toHex(c & 0x0F);
258 });
259 return result;
260 }
261
Dinesh Chinaric576b482017-07-17 16:34:10 -0500262 case keyword::Encoding::UD:
263 {
264 //UD, the UUID info, represented as
265 //123e4567-e89b-12d3-a456-426655440000
266 //<time_low>-<time_mid>-<time hi and version>
267 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id>
268 auto stop = std::next(iterator, UUID_LEN_BYTES);
269 std::string data(iterator, stop);
270 std::string result{};
271 std::for_each(data.cbegin(), data.cend(),
272 [&result](size_t c)
273 {
274 result += toHex(c >> 4);
275 result += toHex(c & 0x0F);
276 });
277 result.insert(UUID_TIME_LOW_END, 1, '-');
278 result.insert(UUID_TIME_MID_END, 1, '-');
279 result.insert(UUID_TIME_HIGH_END, 1, '-');
280 result.insert(UUID_CLK_SEQ_END, 1, '-');
281
282 return result;
283 }
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600284 default:
285 break;
286 }
287
288 return {};
289}
290
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600291internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
292{
293 internal::KeywordMap map {};
294 while (true)
295 {
296 // Note keyword name
297 std::string kw(iterator, iterator + lengths::KW_NAME);
298 if (LAST_KW == kw)
299 {
300 // We're done
301 break;
302 }
303 // Jump past keyword name
304 std::advance(iterator, lengths::KW_NAME);
305 // Note keyword data length
306 std::size_t length = *iterator;
307 // Jump past keyword length
308 std::advance(iterator, sizeof(KwSize));
309 // Pointing to keyword data now
310 if (supportedKeywords.end() != supportedKeywords.find(kw))
311 {
312 // Keyword is of interest to us
313 std::string data = readKwData(
314 (supportedKeywords.find(kw))->second,
315 length,
316 iterator);
317 map.emplace(std::move(kw), std::move(data));
318 }
319 // Jump past keyword data length
320 std::advance(iterator, length);
321 }
322
323 return map;
324}
325
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600326Store Impl::run()
327{
328 // Check if the VHDR record is present
329 checkHeader();
330
331 // Read the table of contents record, to get offsets
332 // to other records.
333 auto offsets = readTOC();
334 for (const auto& offset : offsets)
335 {
336 processRecord(offset);
337 }
338
339 // Return a Store object, which has interfaces to
340 // access parsed VPD by record:keyword
341 return Store(std::move(out));
342}
343
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600344} // namespace parser
345} // namespace vpd
346} // namespace openpower