blob: 9a6d8f988e2469729766a29ac00e594c8451b3fa [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";
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060028
29static const std::unordered_map<std::string,
30 internal::KeywordInfo> supportedKeywords =
31{
32 {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)},
33 {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)},
34 {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)},
35 {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)},
36 {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)},
37 {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)},
38 {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)},
39 {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::RAW)},
40 {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)}
41};
42
Deepak Kodihalli023112f2016-11-22 22:02:14 -060043namespace
44{
45
46using RecordId = uint8_t;
47using RecordOffset = uint16_t;
48using RecordSize = uint16_t;
49using RecordType = uint16_t;
50using RecordLength = uint16_t;
51using KwSize = uint8_t;
52using ECCOffset = uint16_t;
53using ECCLength = uint16_t;
54
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -060055constexpr auto toHex(size_t c)
56{
57 constexpr auto map = "0123456789abcdef";
58 return map[c];
59}
60
Deepak Kodihalli023112f2016-11-22 22:02:14 -060061}
62
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060063namespace offsets
64{
65
66enum Offsets
67{
Deepak Kodihalli023112f2016-11-22 22:02:14 -060068 VHDR = 17,
69 VHDR_TOC_ENTRY = 29,
70 VTOC_PTR = 35,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060071};
72
73}
74
75namespace lengths
76{
77
78enum Lengths
79{
80 RECORD_NAME = 4,
Deepak Kodihalli023112f2016-11-22 22:02:14 -060081 KW_NAME = 2,
Deepak Kodihalli810c9de2016-11-22 11:42:51 -060082 RECORD_MIN = 44,
83};
84
85}
86
87void Impl::checkHeader() const
88{
89 if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
90 {
91 throw std::runtime_error("Malformed VPD");
92 }
93 else
94 {
95 auto iterator = vpd.cbegin();
96 std::advance(iterator, offsets::VHDR);
97 auto stop = std::next(iterator, lengths::RECORD_NAME);
98 std::string record(iterator, stop);
99 if ("VHDR" != record)
100 {
101 throw std::runtime_error("VHDR record not found");
102 }
103 }
104}
105
Deepak Kodihalli023112f2016-11-22 22:02:14 -0600106internal::OffsetList Impl::readTOC() const
107{
108 internal::OffsetList offsets {};
109
110 // The offset to VTOC could be 1 or 2 bytes long
111 RecordOffset vtocOffset = vpd.at(offsets::VTOC_PTR);
112 RecordOffset highByte = vpd.at(offsets::VTOC_PTR + 1);
113 vtocOffset |= (highByte << 8);
114
115 // Got the offset to VTOC, skip past record header and keyword header
116 // to get to the record name.
117 auto iterator = vpd.cbegin();
118 std::advance(iterator,
119 vtocOffset +
120 sizeof(RecordId) +
121 sizeof(RecordSize) +
122 // Skip past the RT keyword, which contains
123 // the record name.
124 lengths::KW_NAME +
125 sizeof(KwSize));
126
127 auto stop = std::next(iterator, lengths::RECORD_NAME);
128 std::string record(iterator, stop);
129 if ("VTOC" != record)
130 {
131 throw std::runtime_error("VTOC record not found");
132 }
133
134 // VTOC record name is good, now read through the TOC, stored in the PT
135 // PT keyword; vpdBuffer is now pointing at the first character of the
136 // name 'VTOC', jump to PT data.
137 // Skip past record name and KW name, 'PT'
138 std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
139 // Note size of PT
140 std::size_t ptLen = *iterator;
141 // Skip past PT size
142 std::advance(iterator, sizeof(KwSize));
143
144 // vpdBuffer is now pointing to PT data
145 return readPT(iterator, ptLen);
146}
147
148internal::OffsetList Impl::readPT(Binary::const_iterator iterator,
149 std::size_t ptLength) const
150{
151 internal::OffsetList offsets{};
152
153 auto end = iterator;
154 std::advance(end, ptLength);
155
156 // Look at each entry in the PT keyword. In the entry,
157 // we care only about the record offset information.
158 while (iterator < end)
159 {
160 // Skip record name and record type
161 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
162
163 // Get record offset
164 RecordOffset offset = *iterator;
165 RecordOffset highByte = *(iterator + 1);
166 offset |= (highByte << 8);
167 offsets.push_back(offset);
168
169 // Jump record size, record length, ECC offset and ECC length
170 std::advance(iterator,
171 sizeof(RecordSize) +
172 sizeof(RecordLength) +
173 sizeof(ECCOffset) +
174 sizeof(ECCLength));
175 }
176
177 return offsets;
178}
179
Deepak Kodihallia1143462016-11-24 06:28:45 -0600180void Impl::processRecord(std::size_t recordOffset)
181{
182 // Jump to record name
183 auto nameOffset = recordOffset +
184 sizeof(RecordId) +
185 sizeof(RecordSize) +
186 // Skip past the RT keyword, which contains
187 // the record name.
188 lengths::KW_NAME +
189 sizeof(KwSize);
190 // Get record name
191 auto iterator = vpd.cbegin();
192 std::advance(iterator, nameOffset);
193
194 std::string name(iterator, iterator + lengths::RECORD_NAME);
195 if (supportedRecords.end() != supportedRecords.find(name))
196 {
197 // 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);
200 auto kwMap = readKeywords(iterator);
201 // Add entry for this record (and contained keyword:value pairs)
202 // to the parsed vpd output.
203 out.emplace(std::move(name), std::move(kwMap));
204 }
205}
206
Deepak Kodihalli4a475bd2016-11-24 07:08:20 -0600207std::string Impl::readKwData(const internal::KeywordInfo& keyword,
208 std::size_t dataLength,
209 Binary::const_iterator iterator)
210{
211 switch (std::get<keyword::Encoding>(keyword))
212 {
213 case keyword::Encoding::ASCII:
214 {
215 auto stop = std::next(iterator, dataLength);
216 return std::string(iterator, stop);
217 }
218
219 case keyword::Encoding::RAW:
220 {
221 auto stop = std::next(iterator, dataLength);
222 std::string data(iterator, stop);
223 std::string result {};
224 std::for_each(data.cbegin(), data.cend(),
225 [&result](size_t c)
226 {
227 result += toHex(c >> 4);
228 result += toHex(c & 0x0F);
229 });
230 return result;
231 }
232
233 case keyword::Encoding::B1:
234 {
235 //B1 is MAC address, represent as AA:BB:CC:DD:EE:FF
236 auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES);
237 std::string data(iterator, stop);
238 std::string result {};
239 auto strItr = data.cbegin();
240 size_t firstDigit = *strItr;
241 result += toHex(firstDigit >> 4);
242 result += toHex(firstDigit & 0x0F);
243 std::advance(strItr, 1);
244 std::for_each(strItr, data.cend(),
245 [&result](size_t c)
246 {
247 result += ":";
248 result += toHex(c >> 4);
249 result += toHex(c & 0x0F);
250 });
251 return result;
252 }
253
254 default:
255 break;
256 }
257
258 return {};
259}
260
Deepak Kodihalli683bf722016-11-24 06:50:43 -0600261internal::KeywordMap Impl::readKeywords(Binary::const_iterator iterator)
262{
263 internal::KeywordMap map {};
264 while (true)
265 {
266 // Note keyword name
267 std::string kw(iterator, iterator + lengths::KW_NAME);
268 if (LAST_KW == kw)
269 {
270 // We're done
271 break;
272 }
273 // Jump past keyword name
274 std::advance(iterator, lengths::KW_NAME);
275 // Note keyword data length
276 std::size_t length = *iterator;
277 // Jump past keyword length
278 std::advance(iterator, sizeof(KwSize));
279 // Pointing to keyword data now
280 if (supportedKeywords.end() != supportedKeywords.find(kw))
281 {
282 // Keyword is of interest to us
283 std::string data = readKwData(
284 (supportedKeywords.find(kw))->second,
285 length,
286 iterator);
287 map.emplace(std::move(kw), std::move(data));
288 }
289 // Jump past keyword data length
290 std::advance(iterator, length);
291 }
292
293 return map;
294}
295
Deepak Kodihalli810c9de2016-11-22 11:42:51 -0600296} // namespace parser
297} // namespace vpd
298} // namespace openpower