blob: 386b164d3c8bf2e5487729e7a3b12c83ee31ab7b [file] [log] [blame]
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05001#include <algorithm>
2#include <map>
3#include <numeric>
4#include "ipmi_fru_info_area.hpp"
5#include <phosphor-logging/elog.hpp>
6namespace ipmi
7{
8namespace fru
9{
10using namespace phosphor::logging;
11
12//Property variables
13static constexpr auto partNumber = "PartNumber";
14static constexpr auto serialNumber = "SerialNumber";
15static constexpr auto manufacturer = "Manufacturer";
16static constexpr auto buildDate = "BuildDate";
17static constexpr auto model = "Model";
18static constexpr auto prettyName = "PrettyName";
19static constexpr auto version = "Version";
20
21//Board info areas
22static constexpr auto board = "Board";
23static constexpr auto chassis = "Chassis";
24static constexpr auto product = "Product";
25
26static constexpr auto specVersion = 0x1;
Ratan Gupta3e6a7692018-02-07 16:10:16 +053027static constexpr auto recordUnitOfMeasurement = 0x8; //size in bytes
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050028static constexpr auto checksumSize = 0x1; //size in bytes
29static constexpr auto recordNotPresent = 0x0;
30static constexpr auto englishLanguageCode = 0x0;
31static constexpr auto typeLengthByteNull = 0x0;
32static constexpr auto endOfCustomFields = 0xC1;
33static constexpr auto commonHeaderFormatSize = 0x8; //size in bytes
34static constexpr auto manufacturingDateSize = 0x3;
35static constexpr auto areaSizeOffset = 0x1;
Ratan Gupta6edfc5c2018-01-31 21:41:45 +053036static constexpr uint8_t typeASCII = 0xC0;
37static constexpr auto maxRecordAttributeValue = 0x1F;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050038
39/**
40 * @brief Format Beginning of Individual IPMI FRU Data Section
41 *
42 * @param[in] langCode Language code
43 * @param[in/out] data FRU area data
44 */
45void preFormatProcessing(bool langCode, FruAreaData& data)
46{
47 //Add id for version of FRU Info Storage Spec used
48 data.emplace_back(specVersion);
49
50 //Add Data Size - 0 as a placeholder, can edit after the data is finalized
51 data.emplace_back(typeLengthByteNull);
52
53 if (langCode)
54 {
55 data.emplace_back(englishLanguageCode);
56 }
57}
58
59/**
60 * @brief Append checksum of the FRU area data
61 *
62 * @param[in/out] data FRU area data
63 */
64void appendDataChecksum(FruAreaData& data)
65{
66 uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0);
67 // Push the Zero checksum as the last byte of this data
68 // This appears to be a simple summation of all the bytes
69 data.emplace_back(-checksumVal);
70}
71
72/**
73 * @brief Append padding bytes for the FRU area data
74 *
75 * @param[in/out] data FRU area data
76 */
77void padData(FruAreaData& data)
78{
Ratan Gupta3e6a7692018-02-07 16:10:16 +053079 uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurement;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050080 if (pad)
81 {
Ratan Gupta3e6a7692018-02-07 16:10:16 +053082 data.resize((data.size() + recordUnitOfMeasurement - pad));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050083 }
84}
85
86/**
87 * @brief Format End of Individual IPMI FRU Data Section
88 *
89 * @param[in/out] fruAreaData FRU area info data
90 */
91void postFormatProcessing(FruAreaData& data)
92{
93 //This area needs to be padded to a multiple of 8 bytes (after checksum)
94 padData(data);
95
96 //Set size of data info area
97 data.at(areaSizeOffset) = (data.size() + checksumSize) /
Ratan Gupta3e6a7692018-02-07 16:10:16 +053098 (recordUnitOfMeasurement);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050099
Ratan Gupta2f66f002018-01-31 21:26:25 +0530100 //Finally add area checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500101 appendDataChecksum(data);
102}
103
104/**
105 * @brief Read property value from inventory and append to the FRU area data
106 *
107 * @param[in] key key to search for in the property inventory data
108 * @param[in] propMap map of property values
109 * @param[in,out] data FRU area data to be appended
110 */
111void appendData(const Property& key, const PropertyMap& propMap,
112 FruAreaData& data)
113{
114 auto iter = propMap.find(key);
115 if (iter != propMap.end())
116 {
117 auto value = iter->second;
118 //If starts with 0x or 0X remove them
119 //ex: 0x123a just take 123a
120 if ((value.compare(0, 2, "0x")) == 0 ||
121 (value.compare(0, 2, "0X") == 0))
122 {
123 value.erase(0, 2);
124 }
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530125
126 // 5 bits for length
127 // if length is greater then 31(2^5) bytes then trim the data to 31 bytess.
128 auto valueLength = (value.length() > maxRecordAttributeValue) ?
129 maxRecordAttributeValue : value.length();
130 // 2 bits for type
131 // Set the type to ascii
132 uint8_t typeLength = valueLength | ipmi::fru::typeASCII;
133
134 data.emplace_back(typeLength);
135 std::copy(value.begin(),
136 value.begin() + valueLength,
137 std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500138 }
139 else
140 {
141 //set 0 size
142 data.emplace_back(typeLengthByteNull);
143 }
144}
145
146
147/**
148 * @brief Appends Build Date
149 *
150 * @param[in] propMap map of property values
151 * @param[in/out] data FRU area to add the manfufacture date
152 */
153void appendMfgDate(const PropertyMap& propMap, FruAreaData& data)
154{
155 //MFG Date/Time
156 auto iter = propMap.find(buildDate);
157 if (iter != propMap.end())
158 {
159 auto& value = iter->second;
160 if (value.length() == manufacturingDateSize)
161 {
162 std::copy(
163 value.begin(), value.end(), std::back_inserter(data));
164 return;
165 }
166 }
167 //Blank date
168 data.emplace_back(0);
169 data.emplace_back(0);
170 data.emplace_back(0);
171}
172
173/**
174 * @brief Builds a section of the common header
175 *
176 * @param[in] infoAreaSize size of the FRU area to write
177 * @param[in] offset Current offset for data in overall record
178 * @param[in/out] data Common Header section data container
179 */
180void buildCommonHeaderSection(
Ratan Gupta2f66f002018-01-31 21:26:25 +0530181 const uint32_t& infoAreaSize, uint16_t& offset, FruAreaData& data)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500182{
183 //Check if data for internal use section populated
184 if (infoAreaSize == 0)
185 {
186 //Indicate record not present
187 data.emplace_back(recordNotPresent);
188 }
189 else
190 {
Ratan Gupta2f66f002018-01-31 21:26:25 +0530191 // offset should be multiple of 8.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530192 auto remainder = offset % recordUnitOfMeasurement;
Ratan Gupta2f66f002018-01-31 21:26:25 +0530193 // add the padding bytes in the offset so that offset
194 // will be multiple of 8 byte.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530195 offset += (remainder > 0) ? recordUnitOfMeasurement - remainder : 0;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500196 //Place data to define offset to area data section
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530197 data.emplace_back(offset / recordUnitOfMeasurement);
Ratan Gupta2f66f002018-01-31 21:26:25 +0530198
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500199 offset += infoAreaSize;
200 }
201}
202
203/**
204 * @brief Builds the Chassis info area data section
205 *
206 * @param[in] propMap map of properties for chassis info area
207 * @return FruAreaData container with chassis info area
208 */
209FruAreaData buildChassisInfoArea(const PropertyMap& propMap)
210{
211 FruAreaData fruAreaData;
212 if (!propMap.empty())
213 {
214 //Set formatting data that goes at the beginning of the record
215 preFormatProcessing(false, fruAreaData);
216
217 //chassis type
218 fruAreaData.emplace_back(0);
219
220 //Chasiss part number, in config.yaml it is configured as model
221 appendData(model, propMap, fruAreaData);
222
223 //Board serial number
224 appendData(serialNumber, propMap, fruAreaData);
225
226 //Indicate End of Custom Fields
227 fruAreaData.emplace_back(endOfCustomFields);
228
229 //Complete record data formatting
230 postFormatProcessing(fruAreaData);
231 }
232 return fruAreaData;
233}
234
235/**
236 * @brief Builds the Board info area data section
237 *
238 * @param[in] propMap map of properties for board info area
239 * @return FruAreaData container with board info area
240 */
241FruAreaData buildBoardInfoArea(const PropertyMap& propMap)
242{
243 FruAreaData fruAreaData;
244 if (!propMap.empty())
245 {
246 preFormatProcessing(true, fruAreaData);
247
248 //Manufacturing date
249 appendMfgDate(propMap, fruAreaData);
250
251 //manufacturer
252 appendData(manufacturer, propMap, fruAreaData);
253
254 //Product name/Pretty name
255 appendData(prettyName, propMap, fruAreaData);
256
257 //Board serial number
258 appendData(serialNumber, propMap, fruAreaData);
259
260 //Board part number
261 appendData(partNumber, propMap, fruAreaData);
262
263 //FRU File ID - Empty
264 fruAreaData.emplace_back(typeLengthByteNull);
265
266 // Empty FRU File ID bytes
267 fruAreaData.emplace_back(recordNotPresent);
268
269 //End of custom fields
270 fruAreaData.emplace_back(endOfCustomFields);
271
272 postFormatProcessing(fruAreaData);
273 }
274 return fruAreaData;
275}
276
277/**
278 * @brief Builds the Product info area data section
279 *
280 * @param[in] propMap map of FRU properties for Board info area
281 * @return FruAreaData container with product info area data
282 */
283FruAreaData buildProductInfoArea(const PropertyMap& propMap)
284{
285 FruAreaData fruAreaData;
286 if (!propMap.empty())
287 {
288 //Set formatting data that goes at the beginning of the record
289 preFormatProcessing(true, fruAreaData);
290
291 //manufacturer
292 appendData(manufacturer, propMap, fruAreaData);
293
294 //Product name/Pretty name
295 appendData(prettyName, propMap, fruAreaData);
296
297 //Product part/model number
298 appendData(model, propMap, fruAreaData);
299
300 //Product version
301 appendData(version, propMap, fruAreaData);
302
303 //Serial Number
304 appendData(serialNumber, propMap, fruAreaData);
305
306 //Add Asset Tag
307 fruAreaData.emplace_back(recordNotPresent);
308
309 //FRU File ID - Empty
310 fruAreaData.emplace_back(typeLengthByteNull);
311
312 // Empty FRU File ID bytes
313 fruAreaData.emplace_back(recordNotPresent);
314
315 //End of custom fields
316 fruAreaData.emplace_back(endOfCustomFields);
317
318 postFormatProcessing(fruAreaData);
319 }
320 return fruAreaData;
321}
322
323FruAreaData buildFruAreaData(const FruInventoryData& inventory)
324{
Ratan Gupta2f66f002018-01-31 21:26:25 +0530325 FruAreaData combFruArea{};
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500326 //Now build common header with data for this FRU Inv Record
327 //Use this variable to increment size of header as we go along to determine
328 //offset for the subsequent area offsets
Ratan Gupta2f66f002018-01-31 21:26:25 +0530329 uint16_t curDataOffset = commonHeaderFormatSize;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500330 //First byte is id for version of FRU Info Storage Spec used
331 combFruArea.emplace_back(specVersion);
332
333 //2nd byte is offset to internal use data
334 combFruArea.emplace_back(recordNotPresent);
335
336 //3rd byte is offset to chassis data
337 FruAreaData chassisArea;
338 auto chassisIt = inventory.find(chassis);
339 if (chassisIt != inventory.end())
340 {
341 chassisArea = std::move(buildChassisInfoArea(chassisIt->second));
342 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530343 // update the offset to chassis data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500344 buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea);
345
346 //4th byte is offset to board data
347 FruAreaData boardArea;
348 auto boardIt = inventory.find(board);
349 if (boardIt != inventory.end())
350 {
351 boardArea = std::move(buildBoardInfoArea(boardIt->second));
352 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530353 // update the offset to the board data.
354 buildCommonHeaderSection(boardArea.size(), curDataOffset, combFruArea);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500355
356 //5th byte is offset to product data
357 FruAreaData prodArea;
358 auto prodIt = inventory.find(product);
359 if (prodIt != inventory.end())
360 {
361 prodArea = std::move(buildProductInfoArea(prodIt->second));
362 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530363 // update the offset to the product data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500364 buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea);
365
366 //6th byte is offset to multirecord data
367 combFruArea.emplace_back(recordNotPresent);
368
369 //7th byte is PAD
Ratan Gupta2f66f002018-01-31 21:26:25 +0530370 combFruArea.emplace_back(recordNotPresent);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500371
372 //8th (Final byte of Header Format) is the checksum
373 appendDataChecksum(combFruArea);
374
375 //Combine everything into one full IPMI FRU specification Record
376 //add chassis use area data
377 combFruArea.insert(
378 combFruArea.end(), chassisArea.begin(), chassisArea.end());
379
380 //add board area data
381 combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end());
382
383 //add product use area data
384 combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end());
385
386 return combFruArea;
387}
388
389} //fru
390} //ipmi