blob: 24b8f0b36b987b489c6a2e289e31d952d51aa996 [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;
27static constexpr auto recordUnitOfMeasurment = 0x8; //size in bytes
28static 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;
36
37/**
38 * @brief Format Beginning of Individual IPMI FRU Data Section
39 *
40 * @param[in] langCode Language code
41 * @param[in/out] data FRU area data
42 */
43void preFormatProcessing(bool langCode, FruAreaData& data)
44{
45 //Add id for version of FRU Info Storage Spec used
46 data.emplace_back(specVersion);
47
48 //Add Data Size - 0 as a placeholder, can edit after the data is finalized
49 data.emplace_back(typeLengthByteNull);
50
51 if (langCode)
52 {
53 data.emplace_back(englishLanguageCode);
54 }
55}
56
57/**
58 * @brief Append checksum of the FRU area data
59 *
60 * @param[in/out] data FRU area data
61 */
62void appendDataChecksum(FruAreaData& data)
63{
64 uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0);
65 // Push the Zero checksum as the last byte of this data
66 // This appears to be a simple summation of all the bytes
67 data.emplace_back(-checksumVal);
68}
69
70/**
71 * @brief Append padding bytes for the FRU area data
72 *
73 * @param[in/out] data FRU area data
74 */
75void padData(FruAreaData& data)
76{
77 uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurment;
78 if (pad)
79 {
80 data.resize((data.size() + recordUnitOfMeasurment - pad));
81 }
82}
83
84/**
85 * @brief Format End of Individual IPMI FRU Data Section
86 *
87 * @param[in/out] fruAreaData FRU area info data
88 */
89void postFormatProcessing(FruAreaData& data)
90{
91 //This area needs to be padded to a multiple of 8 bytes (after checksum)
92 padData(data);
93
94 //Set size of data info area
95 data.at(areaSizeOffset) = (data.size() + checksumSize) /
96 (recordUnitOfMeasurment);
97
Ratan Gupta2f66f002018-01-31 21:26:25 +053098 //Finally add area checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050099 appendDataChecksum(data);
100}
101
102/**
103 * @brief Read property value from inventory and append to the FRU area data
104 *
105 * @param[in] key key to search for in the property inventory data
106 * @param[in] propMap map of property values
107 * @param[in,out] data FRU area data to be appended
108 */
109void appendData(const Property& key, const PropertyMap& propMap,
110 FruAreaData& data)
111{
112 auto iter = propMap.find(key);
113 if (iter != propMap.end())
114 {
115 auto value = iter->second;
116 //If starts with 0x or 0X remove them
117 //ex: 0x123a just take 123a
118 if ((value.compare(0, 2, "0x")) == 0 ||
119 (value.compare(0, 2, "0X") == 0))
120 {
121 value.erase(0, 2);
122 }
123 data.emplace_back(value.length());
124 std::copy(value.begin(), value.end(), std::back_inserter(data));
125 }
126 else
127 {
128 //set 0 size
129 data.emplace_back(typeLengthByteNull);
130 }
131}
132
133
134/**
135 * @brief Appends Build Date
136 *
137 * @param[in] propMap map of property values
138 * @param[in/out] data FRU area to add the manfufacture date
139 */
140void appendMfgDate(const PropertyMap& propMap, FruAreaData& data)
141{
142 //MFG Date/Time
143 auto iter = propMap.find(buildDate);
144 if (iter != propMap.end())
145 {
146 auto& value = iter->second;
147 if (value.length() == manufacturingDateSize)
148 {
149 std::copy(
150 value.begin(), value.end(), std::back_inserter(data));
151 return;
152 }
153 }
154 //Blank date
155 data.emplace_back(0);
156 data.emplace_back(0);
157 data.emplace_back(0);
158}
159
160/**
161 * @brief Builds a section of the common header
162 *
163 * @param[in] infoAreaSize size of the FRU area to write
164 * @param[in] offset Current offset for data in overall record
165 * @param[in/out] data Common Header section data container
166 */
167void buildCommonHeaderSection(
Ratan Gupta2f66f002018-01-31 21:26:25 +0530168 const uint32_t& infoAreaSize, uint16_t& offset, FruAreaData& data)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500169{
170 //Check if data for internal use section populated
171 if (infoAreaSize == 0)
172 {
173 //Indicate record not present
174 data.emplace_back(recordNotPresent);
175 }
176 else
177 {
Ratan Gupta2f66f002018-01-31 21:26:25 +0530178 // offset should be multiple of 8.
179 auto remainder = offset % recordUnitOfMeasurment;
180 // add the padding bytes in the offset so that offset
181 // will be multiple of 8 byte.
182 offset += (remainder > 0) ? recordUnitOfMeasurment - remainder : 0;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500183 //Place data to define offset to area data section
Ratan Gupta2f66f002018-01-31 21:26:25 +0530184 data.emplace_back(offset / recordUnitOfMeasurment);
185
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500186 offset += infoAreaSize;
187 }
188}
189
190/**
191 * @brief Builds the Chassis info area data section
192 *
193 * @param[in] propMap map of properties for chassis info area
194 * @return FruAreaData container with chassis info area
195 */
196FruAreaData buildChassisInfoArea(const PropertyMap& propMap)
197{
198 FruAreaData fruAreaData;
199 if (!propMap.empty())
200 {
201 //Set formatting data that goes at the beginning of the record
202 preFormatProcessing(false, fruAreaData);
203
204 //chassis type
205 fruAreaData.emplace_back(0);
206
207 //Chasiss part number, in config.yaml it is configured as model
208 appendData(model, propMap, fruAreaData);
209
210 //Board serial number
211 appendData(serialNumber, propMap, fruAreaData);
212
213 //Indicate End of Custom Fields
214 fruAreaData.emplace_back(endOfCustomFields);
215
216 //Complete record data formatting
217 postFormatProcessing(fruAreaData);
218 }
219 return fruAreaData;
220}
221
222/**
223 * @brief Builds the Board info area data section
224 *
225 * @param[in] propMap map of properties for board info area
226 * @return FruAreaData container with board info area
227 */
228FruAreaData buildBoardInfoArea(const PropertyMap& propMap)
229{
230 FruAreaData fruAreaData;
231 if (!propMap.empty())
232 {
233 preFormatProcessing(true, fruAreaData);
234
235 //Manufacturing date
236 appendMfgDate(propMap, fruAreaData);
237
238 //manufacturer
239 appendData(manufacturer, propMap, fruAreaData);
240
241 //Product name/Pretty name
242 appendData(prettyName, propMap, fruAreaData);
243
244 //Board serial number
245 appendData(serialNumber, propMap, fruAreaData);
246
247 //Board part number
248 appendData(partNumber, propMap, fruAreaData);
249
250 //FRU File ID - Empty
251 fruAreaData.emplace_back(typeLengthByteNull);
252
253 // Empty FRU File ID bytes
254 fruAreaData.emplace_back(recordNotPresent);
255
256 //End of custom fields
257 fruAreaData.emplace_back(endOfCustomFields);
258
259 postFormatProcessing(fruAreaData);
260 }
261 return fruAreaData;
262}
263
264/**
265 * @brief Builds the Product info area data section
266 *
267 * @param[in] propMap map of FRU properties for Board info area
268 * @return FruAreaData container with product info area data
269 */
270FruAreaData buildProductInfoArea(const PropertyMap& propMap)
271{
272 FruAreaData fruAreaData;
273 if (!propMap.empty())
274 {
275 //Set formatting data that goes at the beginning of the record
276 preFormatProcessing(true, fruAreaData);
277
278 //manufacturer
279 appendData(manufacturer, propMap, fruAreaData);
280
281 //Product name/Pretty name
282 appendData(prettyName, propMap, fruAreaData);
283
284 //Product part/model number
285 appendData(model, propMap, fruAreaData);
286
287 //Product version
288 appendData(version, propMap, fruAreaData);
289
290 //Serial Number
291 appendData(serialNumber, propMap, fruAreaData);
292
293 //Add Asset Tag
294 fruAreaData.emplace_back(recordNotPresent);
295
296 //FRU File ID - Empty
297 fruAreaData.emplace_back(typeLengthByteNull);
298
299 // Empty FRU File ID bytes
300 fruAreaData.emplace_back(recordNotPresent);
301
302 //End of custom fields
303 fruAreaData.emplace_back(endOfCustomFields);
304
305 postFormatProcessing(fruAreaData);
306 }
307 return fruAreaData;
308}
309
310FruAreaData buildFruAreaData(const FruInventoryData& inventory)
311{
Ratan Gupta2f66f002018-01-31 21:26:25 +0530312 FruAreaData combFruArea{};
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500313 //Now build common header with data for this FRU Inv Record
314 //Use this variable to increment size of header as we go along to determine
315 //offset for the subsequent area offsets
Ratan Gupta2f66f002018-01-31 21:26:25 +0530316 uint16_t curDataOffset = commonHeaderFormatSize;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500317 //First byte is id for version of FRU Info Storage Spec used
318 combFruArea.emplace_back(specVersion);
319
320 //2nd byte is offset to internal use data
321 combFruArea.emplace_back(recordNotPresent);
322
323 //3rd byte is offset to chassis data
324 FruAreaData chassisArea;
325 auto chassisIt = inventory.find(chassis);
326 if (chassisIt != inventory.end())
327 {
328 chassisArea = std::move(buildChassisInfoArea(chassisIt->second));
329 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530330 // update the offset to chassis data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500331 buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea);
332
333 //4th byte is offset to board data
334 FruAreaData boardArea;
335 auto boardIt = inventory.find(board);
336 if (boardIt != inventory.end())
337 {
338 boardArea = std::move(buildBoardInfoArea(boardIt->second));
339 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530340 // update the offset to the board data.
341 buildCommonHeaderSection(boardArea.size(), curDataOffset, combFruArea);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500342
343 //5th byte is offset to product data
344 FruAreaData prodArea;
345 auto prodIt = inventory.find(product);
346 if (prodIt != inventory.end())
347 {
348 prodArea = std::move(buildProductInfoArea(prodIt->second));
349 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530350 // update the offset to the product data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500351 buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea);
352
353 //6th byte is offset to multirecord data
354 combFruArea.emplace_back(recordNotPresent);
355
356 //7th byte is PAD
Ratan Gupta2f66f002018-01-31 21:26:25 +0530357 combFruArea.emplace_back(recordNotPresent);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500358
359 //8th (Final byte of Header Format) is the checksum
360 appendDataChecksum(combFruArea);
361
362 //Combine everything into one full IPMI FRU specification Record
363 //add chassis use area data
364 combFruArea.insert(
365 combFruArea.end(), chassisArea.begin(), chassisArea.end());
366
367 //add board area data
368 combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end());
369
370 //add product use area data
371 combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end());
372
373 return combFruArea;
374}
375
376} //fru
377} //ipmi