blob: 7ac147d108049e235a0d0aea15ee349cbc6ca959 [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)},
46 {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)}
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060047};
48
Deepak Kodihalli023112f2016-11-22 22:02:14 -060049namespace
50{
51
52using RecordId = uint8_t;
53using RecordOffset = uint16_t;
54using RecordSize = uint16_t;
55using RecordType = uint16_t;
56using RecordLength = uint16_t;
57using KwSize = uint8_t;
58using ECCOffset = uint16_t;
59using ECCLength = uint16_t;
60
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060061constexpr auto toHex(size_t c)
62{
63 constexpr auto map = "0123456789abcdef";
64 return map[c];
65}
66
Deepak Kodihalli023112f2016-11-22 22:02:14 -060067}
68
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060069namespace offsets
70{
71
72enum Offsets
73{
Deepak Kodihalli023112f2016-11-22 22:02:14 -060074 VHDR = 17,
75 VHDR_TOC_ENTRY = 29,
76 VTOC_PTR = 35,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060077};
78
79}
80
81namespace lengths
82{
83
84enum Lengths
85{
86 RECORD_NAME = 4,
Deepak Kodihalli023112f2016-11-22 22:02:14 -060087 KW_NAME = 2,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060088 RECORD_MIN = 44,
89};
90
91}
92
93void Impl::checkHeader() const
94{
95 if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
96 {
97 throw std::runtime_error("Malformed VPD");
98 }
99 else
100 {
101 auto iterator = vpd.cbegin();
102 std::advance(iterator, offsets::VHDR);
103 auto stop = std::next(iterator, lengths::RECORD_NAME);
104 std::string record(iterator, stop);
105 if ("VHDR" != record)
106 {
107 throw std::runtime_error("VHDR record not found");
108 }
109 }
110}
111
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600112internal::OffsetList Impl::readTOC() const
113{
114 internal::OffsetList offsets {};
115
116 // The offset to VTOC could be 1 or 2 bytes long
117 RecordOffset vtocOffset = vpd.at(offsets::VTOC_PTR);
118 RecordOffset highByte = vpd.at(offsets::VTOC_PTR + 1);
119 vtocOffset |= (highByte << 8);
120
121 // Got the offset to VTOC, skip past record header and keyword header
122 // to get to the record name.
123 auto iterator = vpd.cbegin();
124 std::advance(iterator,
125 vtocOffset +
126 sizeof(RecordId) +
127 sizeof(RecordSize) +
128 // Skip past the RT keyword, which contains
129 // the record name.
130 lengths::KW_NAME +
131 sizeof(KwSize));
132
133 auto stop = std::next(iterator, lengths::RECORD_NAME);
134 std::string record(iterator, stop);
135 if ("VTOC" != record)
136 {
137 throw std::runtime_error("VTOC record not found");
138 }
139
140 // VTOC record name is good, now read through the TOC, stored in the PT
141 // PT keyword; vpdBuffer is now pointing at the first character of the
142 // name 'VTOC', jump to PT data.
143 // Skip past record name and KW name, 'PT'
144 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
145 // Note size of PT
146 std::size_t ptLen = *iterator;
147 // Skip past PT size
148 std::advance(iterator, sizeof(KwSize));
149
150 // vpdBuffer is now pointing to PT data
151 return readPT(iterator, ptLen);
152}
153
154internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
155 std::size_t ptLength) const
156{
157 internal::OffsetList offsets{};
158
159 auto end = iterator;
160 std::advance(end, ptLength);
161
162 // Look at each entry in the PT keyword. In the entry,
163 // we care only about the record offset information.
164 while (iterator < end)
165 {
166 // Skip record name and record type
167 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
168
169 // Get record offset
170 RecordOffset offset = *iterator;
171 RecordOffset highByte = *(iterator + 1);
172 offset |= (highByte << 8);
173 offsets.push_back(offset);
174
175 // Jump record size, record length, ECC offset and ECC length
176 std::advance(iterator,
177 sizeof(RecordSize) +
178 sizeof(RecordLength) +
179 sizeof(ECCOffset) +
180 sizeof(ECCLength));
181 }
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
189 auto nameOffset = recordOffset +
190 sizeof(RecordId) +
191 sizeof(RecordSize) +
192 // Skip past the RT keyword, which contains
193 // the record name.
194 lengths::KW_NAME +
195 sizeof(KwSize);
196 // Get record name
197 auto iterator = vpd.cbegin();
198 std::advance(iterator, nameOffset);
199
200 std::string name(iterator, iterator + lengths::RECORD_NAME);
201 if (supportedRecords.end() != supportedRecords.find(name))
202 {
203 // If it's a record we're interested in, proceed to find
204 // contained keywords and their values.
205 std::advance(iterator, lengths::RECORD_NAME);
206 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));
210 }
211}
212
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600213std::string Impl::readKwData(const internal::KeywordInfo& keyword,
214 std::size_t dataLength,
215 Binary::const_iterator iterator)
216{
217 switch (std::get<keyword::Encoding>(keyword))
218 {
219 case keyword::Encoding::ASCII:
220 {
221 auto stop = std::next(iterator, dataLength);
222 return std::string(iterator, stop);
223 }
224
225 case keyword::Encoding::RAW:
226 {
227 auto stop = std::next(iterator, dataLength);
228 std::string data(iterator, stop);
229 std::string result {};
230 std::for_each(data.cbegin(), data.cend(),
231 [&result](size_t c)
232 {
233 result += toHex(c >> 4);
234 result += toHex(c & 0x0F);
235 });
236 return result;
237 }
238
239 case keyword::Encoding::B1:
240 {
241 //B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
242 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
243 std::string data(iterator, stop);
244 std::string result {};
245 auto strItr = data.cbegin();
246 size_t firstDigit = *strItr;
247 result += toHex(firstDigit >> 4);
248 result += toHex(firstDigit & 0x0F);
249 std::advance(strItr, 1);
250 std::for_each(strItr, data.cend(),
251 [&result](size_t c)
252 {
253 result += ":";
254 result += toHex(c >> 4);
255 result += toHex(c & 0x0F);
256 });
257 return result;
258 }
259
Dinesh Chinaric576b482017-07-17 16:34:10 -0500260 case keyword::Encoding::UD:
261 {
262 //UD, the UUID info, represented as
263 //123e4567-e89b-12d3-a456-426655440000
264 //<time_low>-<time_mid>-<time hi and version>
265 //-<clock_seq_hi_and_res clock_seq_low>-<48 bits node id>
266 auto stop = std::next(iterator, UUID_LEN_BYTES);
267 std::string data(iterator, stop);
268 std::string result{};
269 std::for_each(data.cbegin(), data.cend(),
270 [&result](size_t c)
271 {
272 result += toHex(c >> 4);
273 result += toHex(c & 0x0F);
274 });
275 result.insert(UUID_TIME_LOW_END, 1, '-');
276 result.insert(UUID_TIME_MID_END, 1, '-');
277 result.insert(UUID_TIME_HIGH_END, 1, '-');
278 result.insert(UUID_CLK_SEQ_END, 1, '-');
279
280 return result;
281 }
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600282 default:
283 break;
284 }
285
286 return {};
287}
288
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600289internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
290{
291 internal::KeywordMap map {};
292 while (true)
293 {
294 // Note keyword name
295 std::string kw(iterator, iterator + lengths::KW_NAME);
296 if (LAST_KW == kw)
297 {
298 // We're done
299 break;
300 }
301 // Jump past keyword name
302 std::advance(iterator, lengths::KW_NAME);
303 // Note keyword data length
304 std::size_t length = *iterator;
305 // Jump past keyword length
306 std::advance(iterator, sizeof(KwSize));
307 // Pointing to keyword data now
308 if (supportedKeywords.end() != supportedKeywords.find(kw))
309 {
310 // Keyword is of interest to us
311 std::string data = readKwData(
312 (supportedKeywords.find(kw))->second,
313 length,
314 iterator);
315 map.emplace(std::move(kw), std::move(data));
316 }
317 // Jump past keyword data length
318 std::advance(iterator, length);
319 }
320
321 return map;
322}
323
Deepak Kodihalli174caf62016-11-25 05:41:19 -0600324Store Impl::run()
325{
326 // Check if the VHDR record is present
327 checkHeader();
328
329 // Read the table of contents record, to get offsets
330 // to other records.
331 auto offsets = readTOC();
332 for (const auto& offset : offsets)
333 {
334 processRecord(offset);
335 }
336
337 // Return a Store object, which has interfaces to
338 // access parsed VPD by record:keyword
339 return Store(std::move(out));
340}
341
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600342} // namespace parser
343} // namespace vpd
344} // namespace openpower