blob: 85d200796af7d9b6e56dba5966190b35b0b1843e [file] [log] [blame]
Patrick Venture0b02be92018-08-31 11:55:55 -07001#include "ipmi_fru_info_area.hpp"
2
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05003#include <algorithm>
Patrick Venture0b02be92018-08-31 11:55:55 -07004#include <ctime>
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05005#include <map>
6#include <numeric>
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05007#include <phosphor-logging/elog.hpp>
Patrick Ventureb51bf9c2018-09-10 15:53:14 -07008
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05009namespace ipmi
10{
11namespace fru
12{
13using namespace phosphor::logging;
14
Patrick Venture0b02be92018-08-31 11:55:55 -070015// Property variables
Patrick Venture45e93cb2019-07-25 15:03:19 -070016static constexpr auto partNumber = "Part Number";
17static constexpr auto serialNumber = "Serial Number";
Patrick Venture0b02be92018-08-31 11:55:55 -070018static constexpr auto manufacturer = "Manufacturer";
Patrick Venture45e93cb2019-07-25 15:03:19 -070019static constexpr auto buildDate = "Mfg Date";
Patrick Venture0b02be92018-08-31 11:55:55 -070020static constexpr auto model = "Model";
Patrick Venture45e93cb2019-07-25 15:03:19 -070021static constexpr auto prettyName = "Name";
Patrick Venture0b02be92018-08-31 11:55:55 -070022static constexpr auto version = "Version";
Oskar Senft40f59e22018-12-04 22:43:19 -050023static constexpr auto type = "Type";
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050024
Patrick Venture0b02be92018-08-31 11:55:55 -070025// Board info areas
26static constexpr auto board = "Board";
27static constexpr auto chassis = "Chassis";
28static constexpr auto product = "Product";
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050029
Patrick Venture0b02be92018-08-31 11:55:55 -070030static constexpr auto specVersion = 0x1;
31static constexpr auto recordUnitOfMeasurement = 0x8; // size in bytes
32static constexpr auto checksumSize = 0x1; // size in bytes
33static constexpr auto recordNotPresent = 0x0;
34static constexpr auto englishLanguageCode = 0x0;
35static constexpr auto typeLengthByteNull = 0x0;
36static constexpr auto endOfCustomFields = 0xC1;
37static constexpr auto commonHeaderFormatSize = 0x8; // size in bytes
38static constexpr auto manufacturingDateSize = 0x3;
39static constexpr auto areaSizeOffset = 0x1;
40static constexpr uint8_t typeASCII = 0xC0;
41static constexpr auto maxRecordAttributeValue = 0x1F;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050042
Andres Oportus7ebd2462018-04-09 10:35:21 -070043static constexpr auto secs_from_1970_1996 = 820454400;
Patrick Venture0b02be92018-08-31 11:55:55 -070044static constexpr auto maxMfgDateValue = 0xFFFFFF; // 3 Byte length
Andres Oportus7ebd2462018-04-09 10:35:21 -070045static constexpr auto secs_per_min = 60;
Patrick Venture0b02be92018-08-31 11:55:55 -070046static constexpr auto secsToMaxMfgdate =
47 secs_from_1970_1996 + secs_per_min * maxMfgDateValue;
Andres Oportus7ebd2462018-04-09 10:35:21 -070048
Oskar Senft8b189412018-12-07 16:13:38 -050049// Minimum size of resulting FRU blob.
50// This is also the theoretical maximum size according to the spec:
51// 8 bytes header + 5 areas at 0xff*8 bytes max each
52// 8 + 5*0xff*8 = 0x27e0
53static constexpr auto fruMinSize = 0x27E0;
54
55// Value to use for padding.
56// Using 0xff to match the default (blank) value in a physical EEPROM.
57static constexpr auto fruPadValue = 0xff;
58
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050059/**
60 * @brief Format Beginning of Individual IPMI FRU Data Section
61 *
62 * @param[in] langCode Language code
63 * @param[in/out] data FRU area data
64 */
65void preFormatProcessing(bool langCode, FruAreaData& data)
66{
Patrick Venture0b02be92018-08-31 11:55:55 -070067 // Add id for version of FRU Info Storage Spec used
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050068 data.emplace_back(specVersion);
69
Patrick Venture0b02be92018-08-31 11:55:55 -070070 // Add Data Size - 0 as a placeholder, can edit after the data is finalized
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050071 data.emplace_back(typeLengthByteNull);
72
73 if (langCode)
74 {
75 data.emplace_back(englishLanguageCode);
76 }
77}
78
79/**
80 * @brief Append checksum of the FRU area data
81 *
82 * @param[in/out] data FRU area data
83 */
84void appendDataChecksum(FruAreaData& data)
85{
86 uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0);
87 // Push the Zero checksum as the last byte of this data
88 // This appears to be a simple summation of all the bytes
89 data.emplace_back(-checksumVal);
90}
91
92/**
93 * @brief Append padding bytes for the FRU area data
94 *
95 * @param[in/out] data FRU area data
96 */
97void padData(FruAreaData& data)
98{
Ratan Gupta3e6a7692018-02-07 16:10:16 +053099 uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurement;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500100 if (pad)
101 {
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530102 data.resize((data.size() + recordUnitOfMeasurement - pad));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500103 }
104}
105
106/**
107 * @brief Format End of Individual IPMI FRU Data Section
108 *
109 * @param[in/out] fruAreaData FRU area info data
110 */
111void postFormatProcessing(FruAreaData& data)
112{
Patrick Venture0b02be92018-08-31 11:55:55 -0700113 // This area needs to be padded to a multiple of 8 bytes (after checksum)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500114 padData(data);
115
Patrick Venture0b02be92018-08-31 11:55:55 -0700116 // Set size of data info area
117 data.at(areaSizeOffset) =
118 (data.size() + checksumSize) / (recordUnitOfMeasurement);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500119
Patrick Venture0b02be92018-08-31 11:55:55 -0700120 // Finally add area checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500121 appendDataChecksum(data);
122}
123
124/**
Oskar Senft40f59e22018-12-04 22:43:19 -0500125 * @brief Read chassis type property value from inventory and append to the FRU
126 * area data.
127 *
128 * @param[in] propMap map of property values
129 * @param[in,out] data FRU area data to be appended
130 */
131void appendChassisType(const PropertyMap& propMap, FruAreaData& data)
132{
133 uint8_t chassisType = 0; // Not specified
134 auto iter = propMap.find(type);
135 if (iter != propMap.end())
136 {
137 auto value = iter->second;
Oskar Senftcf059392018-12-17 16:26:32 -0500138 try
139 {
140 chassisType = std::stoi(value);
141 }
142 catch (std::exception& e)
143 {
144 log<level::ERR>("Could not parse chassis type",
145 entry("VALUE=%s", value.c_str()),
146 entry("ERROR=%s", e.what()));
147 chassisType = 0;
148 }
Oskar Senft40f59e22018-12-04 22:43:19 -0500149 }
150 data.emplace_back(chassisType);
151}
152
153/**
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500154 * @brief Read property value from inventory and append to the FRU area data
155 *
156 * @param[in] key key to search for in the property inventory data
157 * @param[in] propMap map of property values
158 * @param[in,out] data FRU area data to be appended
159 */
160void appendData(const Property& key, const PropertyMap& propMap,
161 FruAreaData& data)
162{
163 auto iter = propMap.find(key);
164 if (iter != propMap.end())
165 {
166 auto value = iter->second;
Patrick Venture0b02be92018-08-31 11:55:55 -0700167 // If starts with 0x or 0X remove them
168 // ex: 0x123a just take 123a
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500169 if ((value.compare(0, 2, "0x")) == 0 ||
Patrick Venture0b02be92018-08-31 11:55:55 -0700170 (value.compare(0, 2, "0X") == 0))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500171 {
172 value.erase(0, 2);
173 }
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530174
175 // 5 bits for length
Patrick Venture0b02be92018-08-31 11:55:55 -0700176 // if length is greater then 31(2^5) bytes then trim the data to 31
177 // bytess.
178 auto valueLength = (value.length() > maxRecordAttributeValue)
179 ? maxRecordAttributeValue
180 : value.length();
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530181 // 2 bits for type
182 // Set the type to ascii
183 uint8_t typeLength = valueLength | ipmi::fru::typeASCII;
184
185 data.emplace_back(typeLength);
Patrick Venture0b02be92018-08-31 11:55:55 -0700186 std::copy(value.begin(), value.begin() + valueLength,
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530187 std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500188 }
189 else
190 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700191 // set 0 size
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500192 data.emplace_back(typeLengthByteNull);
193 }
194}
195
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500196/**
197 * @brief Appends Build Date
198 *
199 * @param[in] propMap map of property values
200 * @param[in/out] data FRU area to add the manfufacture date
201 */
202void appendMfgDate(const PropertyMap& propMap, FruAreaData& data)
203{
Patrick Venture0b02be92018-08-31 11:55:55 -0700204 // MFG Date/Time
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500205 auto iter = propMap.find(buildDate);
Nagaraju Gorugantib898cde2018-07-10 00:47:43 -0500206 if ((iter != propMap.end()) && (iter->second.size() > 0))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500207 {
Andres Oportus7ebd2462018-04-09 10:35:21 -0700208 tm time = {};
209 strptime(iter->second.c_str(), "%F - %H:%M:%S", &time);
210 time_t raw = mktime(&time);
211
212 // From FRU Spec:
213 // "Mfg. Date / Time
214 // Number of minutes from 0:00 hrs 1/1/96.
215 // LSbyte first (little endian)
216 // 00_00_00h = unspecified."
Nagaraju Gorugantib898cde2018-07-10 00:47:43 -0500217 if ((raw >= secs_from_1970_1996) && (raw <= secsToMaxMfgdate))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500218 {
Andres Oportus7ebd2462018-04-09 10:35:21 -0700219 raw -= secs_from_1970_1996;
220 raw /= secs_per_min;
221 uint8_t fru_raw[3];
222 fru_raw[0] = raw & 0xFF;
223 fru_raw[1] = (raw >> 8) & 0xFF;
224 fru_raw[2] = (raw >> 16) & 0xFF;
225 std::copy(fru_raw, fru_raw + 3, std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500226 return;
227 }
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700228 std::fprintf(stderr, "MgfDate invalid date: %u secs since UNIX epoch\n",
229 static_cast<unsigned int>(raw));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500230 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700231 // Blank date
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500232 data.emplace_back(0);
233 data.emplace_back(0);
234 data.emplace_back(0);
235}
236
237/**
238 * @brief Builds a section of the common header
239 *
240 * @param[in] infoAreaSize size of the FRU area to write
241 * @param[in] offset Current offset for data in overall record
242 * @param[in/out] data Common Header section data container
243 */
Patrick Venture0b02be92018-08-31 11:55:55 -0700244void buildCommonHeaderSection(const uint32_t& infoAreaSize, uint16_t& offset,
245 FruAreaData& data)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500246{
Patrick Venture0b02be92018-08-31 11:55:55 -0700247 // Check if data for internal use section populated
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500248 if (infoAreaSize == 0)
249 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700250 // Indicate record not present
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500251 data.emplace_back(recordNotPresent);
252 }
253 else
254 {
Ratan Gupta2f66f002018-01-31 21:26:25 +0530255 // offset should be multiple of 8.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530256 auto remainder = offset % recordUnitOfMeasurement;
Ratan Gupta2f66f002018-01-31 21:26:25 +0530257 // add the padding bytes in the offset so that offset
258 // will be multiple of 8 byte.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530259 offset += (remainder > 0) ? recordUnitOfMeasurement - remainder : 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700260 // Place data to define offset to area data section
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530261 data.emplace_back(offset / recordUnitOfMeasurement);
Ratan Gupta2f66f002018-01-31 21:26:25 +0530262
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500263 offset += infoAreaSize;
264 }
265}
266
267/**
268 * @brief Builds the Chassis info area data section
269 *
270 * @param[in] propMap map of properties for chassis info area
271 * @return FruAreaData container with chassis info area
272 */
273FruAreaData buildChassisInfoArea(const PropertyMap& propMap)
274{
275 FruAreaData fruAreaData;
276 if (!propMap.empty())
277 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700278 // Set formatting data that goes at the beginning of the record
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500279 preFormatProcessing(false, fruAreaData);
280
Patrick Venture0b02be92018-08-31 11:55:55 -0700281 // chassis type
Oskar Senft40f59e22018-12-04 22:43:19 -0500282 appendChassisType(propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500283
Patrick Venture0b02be92018-08-31 11:55:55 -0700284 // Chasiss part number, in config.yaml it is configured as model
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500285 appendData(model, propMap, fruAreaData);
286
Patrick Venture0b02be92018-08-31 11:55:55 -0700287 // Board serial number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500288 appendData(serialNumber, propMap, fruAreaData);
289
Patrick Venture0b02be92018-08-31 11:55:55 -0700290 // Indicate End of Custom Fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500291 fruAreaData.emplace_back(endOfCustomFields);
292
Patrick Venture0b02be92018-08-31 11:55:55 -0700293 // Complete record data formatting
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500294 postFormatProcessing(fruAreaData);
295 }
296 return fruAreaData;
297}
298
299/**
300 * @brief Builds the Board info area data section
301 *
302 * @param[in] propMap map of properties for board info area
303 * @return FruAreaData container with board info area
304 */
305FruAreaData buildBoardInfoArea(const PropertyMap& propMap)
306{
307 FruAreaData fruAreaData;
308 if (!propMap.empty())
309 {
310 preFormatProcessing(true, fruAreaData);
311
Patrick Venture0b02be92018-08-31 11:55:55 -0700312 // Manufacturing date
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500313 appendMfgDate(propMap, fruAreaData);
314
Patrick Venture0b02be92018-08-31 11:55:55 -0700315 // manufacturer
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500316 appendData(manufacturer, propMap, fruAreaData);
317
Patrick Venture0b02be92018-08-31 11:55:55 -0700318 // Product name/Pretty name
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500319 appendData(prettyName, propMap, fruAreaData);
320
Patrick Venture0b02be92018-08-31 11:55:55 -0700321 // Board serial number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500322 appendData(serialNumber, propMap, fruAreaData);
323
Patrick Venture0b02be92018-08-31 11:55:55 -0700324 // Board part number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500325 appendData(partNumber, propMap, fruAreaData);
326
Patrick Venture0b02be92018-08-31 11:55:55 -0700327 // FRU File ID - Empty
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500328 fruAreaData.emplace_back(typeLengthByteNull);
329
330 // Empty FRU File ID bytes
331 fruAreaData.emplace_back(recordNotPresent);
332
Patrick Venture0b02be92018-08-31 11:55:55 -0700333 // End of custom fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500334 fruAreaData.emplace_back(endOfCustomFields);
335
336 postFormatProcessing(fruAreaData);
337 }
338 return fruAreaData;
339}
340
341/**
342 * @brief Builds the Product info area data section
343 *
344 * @param[in] propMap map of FRU properties for Board info area
345 * @return FruAreaData container with product info area data
346 */
347FruAreaData buildProductInfoArea(const PropertyMap& propMap)
348{
349 FruAreaData fruAreaData;
350 if (!propMap.empty())
351 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700352 // Set formatting data that goes at the beginning of the record
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500353 preFormatProcessing(true, fruAreaData);
354
Patrick Venture0b02be92018-08-31 11:55:55 -0700355 // manufacturer
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500356 appendData(manufacturer, propMap, fruAreaData);
357
Patrick Venture0b02be92018-08-31 11:55:55 -0700358 // Product name/Pretty name
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500359 appendData(prettyName, propMap, fruAreaData);
360
Patrick Venture0b02be92018-08-31 11:55:55 -0700361 // Product part/model number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500362 appendData(model, propMap, fruAreaData);
363
Patrick Venture0b02be92018-08-31 11:55:55 -0700364 // Product version
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500365 appendData(version, propMap, fruAreaData);
366
Patrick Venture0b02be92018-08-31 11:55:55 -0700367 // Serial Number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500368 appendData(serialNumber, propMap, fruAreaData);
369
Patrick Venture0b02be92018-08-31 11:55:55 -0700370 // Add Asset Tag
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500371 fruAreaData.emplace_back(recordNotPresent);
372
Patrick Venture0b02be92018-08-31 11:55:55 -0700373 // FRU File ID - Empty
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500374 fruAreaData.emplace_back(typeLengthByteNull);
375
376 // Empty FRU File ID bytes
377 fruAreaData.emplace_back(recordNotPresent);
378
Patrick Venture0b02be92018-08-31 11:55:55 -0700379 // End of custom fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500380 fruAreaData.emplace_back(endOfCustomFields);
381
382 postFormatProcessing(fruAreaData);
383 }
384 return fruAreaData;
385}
386
387FruAreaData buildFruAreaData(const FruInventoryData& inventory)
388{
Ratan Gupta2f66f002018-01-31 21:26:25 +0530389 FruAreaData combFruArea{};
Patrick Venture0b02be92018-08-31 11:55:55 -0700390 // Now build common header with data for this FRU Inv Record
391 // Use this variable to increment size of header as we go along to determine
392 // offset for the subsequent area offsets
Ratan Gupta2f66f002018-01-31 21:26:25 +0530393 uint16_t curDataOffset = commonHeaderFormatSize;
Patrick Venture0b02be92018-08-31 11:55:55 -0700394 // First byte is id for version of FRU Info Storage Spec used
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500395 combFruArea.emplace_back(specVersion);
396
Patrick Venture0b02be92018-08-31 11:55:55 -0700397 // 2nd byte is offset to internal use data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500398 combFruArea.emplace_back(recordNotPresent);
399
Patrick Venture0b02be92018-08-31 11:55:55 -0700400 // 3rd byte is offset to chassis data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500401 FruAreaData chassisArea;
402 auto chassisIt = inventory.find(chassis);
403 if (chassisIt != inventory.end())
404 {
405 chassisArea = std::move(buildChassisInfoArea(chassisIt->second));
406 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530407 // update the offset to chassis data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500408 buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea);
409
Patrick Venture0b02be92018-08-31 11:55:55 -0700410 // 4th byte is offset to board data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500411 FruAreaData boardArea;
412 auto boardIt = inventory.find(board);
413 if (boardIt != inventory.end())
414 {
415 boardArea = std::move(buildBoardInfoArea(boardIt->second));
416 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530417 // update the offset to the board data.
418 buildCommonHeaderSection(boardArea.size(), curDataOffset, combFruArea);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500419
Patrick Venture0b02be92018-08-31 11:55:55 -0700420 // 5th byte is offset to product data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500421 FruAreaData prodArea;
422 auto prodIt = inventory.find(product);
423 if (prodIt != inventory.end())
424 {
425 prodArea = std::move(buildProductInfoArea(prodIt->second));
426 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530427 // update the offset to the product data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500428 buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea);
429
Patrick Venture0b02be92018-08-31 11:55:55 -0700430 // 6th byte is offset to multirecord data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500431 combFruArea.emplace_back(recordNotPresent);
432
Patrick Venture0b02be92018-08-31 11:55:55 -0700433 // 7th byte is PAD
Ratan Gupta2f66f002018-01-31 21:26:25 +0530434 combFruArea.emplace_back(recordNotPresent);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500435
Patrick Venture0b02be92018-08-31 11:55:55 -0700436 // 8th (Final byte of Header Format) is the checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500437 appendDataChecksum(combFruArea);
438
Patrick Venture0b02be92018-08-31 11:55:55 -0700439 // Combine everything into one full IPMI FRU specification Record
440 // add chassis use area data
441 combFruArea.insert(combFruArea.end(), chassisArea.begin(),
442 chassisArea.end());
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500443
Patrick Venture0b02be92018-08-31 11:55:55 -0700444 // add board area data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500445 combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end());
446
Patrick Venture0b02be92018-08-31 11:55:55 -0700447 // add product use area data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500448 combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end());
449
Oskar Senft8b189412018-12-07 16:13:38 -0500450 // If area is smaller than the minimum size, pad it. This enables ipmitool
451 // to update the FRU blob with values longer than the original payload.
452 if (combFruArea.size() < fruMinSize)
453 {
454 combFruArea.resize(fruMinSize, fruPadValue);
455 }
456
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500457 return combFruArea;
458}
459
Patrick Venture0b02be92018-08-31 11:55:55 -0700460} // namespace fru
461} // namespace ipmi