blob: 155e88b26a057099536afecf9c36cc91e871abc9 [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>
Patrick Venture24c771c2019-07-25 16:34:41 -07005#include <iomanip>
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05006#include <map>
7#include <numeric>
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05008#include <phosphor-logging/elog.hpp>
Patrick Venture24c771c2019-07-25 16:34:41 -07009#include <sstream>
Patrick Ventureb51bf9c2018-09-10 15:53:14 -070010
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050011namespace ipmi
12{
13namespace fru
14{
15using namespace phosphor::logging;
16
Patrick Venture0b02be92018-08-31 11:55:55 -070017// Property variables
Patrick Venture45e93cb2019-07-25 15:03:19 -070018static constexpr auto partNumber = "Part Number";
19static constexpr auto serialNumber = "Serial Number";
Patrick Venture0b02be92018-08-31 11:55:55 -070020static constexpr auto manufacturer = "Manufacturer";
Patrick Venture45e93cb2019-07-25 15:03:19 -070021static constexpr auto buildDate = "Mfg Date";
William A. Kennington IIIecd9f372020-05-31 15:52:09 -070022static constexpr auto modelNumber = "Model Number";
Patrick Venture45e93cb2019-07-25 15:03:19 -070023static constexpr auto prettyName = "Name";
Patrick Venture0b02be92018-08-31 11:55:55 -070024static constexpr auto version = "Version";
Oskar Senft40f59e22018-12-04 22:43:19 -050025static constexpr auto type = "Type";
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050026
Patrick Venture0b02be92018-08-31 11:55:55 -070027// Board info areas
28static constexpr auto board = "Board";
29static constexpr auto chassis = "Chassis";
30static constexpr auto product = "Product";
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050031
Patrick Venture0b02be92018-08-31 11:55:55 -070032static constexpr auto specVersion = 0x1;
33static constexpr auto recordUnitOfMeasurement = 0x8; // size in bytes
34static constexpr auto checksumSize = 0x1; // size in bytes
35static constexpr auto recordNotPresent = 0x0;
36static constexpr auto englishLanguageCode = 0x0;
37static constexpr auto typeLengthByteNull = 0x0;
38static constexpr auto endOfCustomFields = 0xC1;
39static constexpr auto commonHeaderFormatSize = 0x8; // size in bytes
40static constexpr auto manufacturingDateSize = 0x3;
41static constexpr auto areaSizeOffset = 0x1;
42static constexpr uint8_t typeASCII = 0xC0;
Kirill Pakhomove1160cb2020-03-24 17:08:38 +030043static constexpr auto maxRecordAttributeValue = 0x3F;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050044
Andres Oportus7ebd2462018-04-09 10:35:21 -070045static constexpr auto secs_from_1970_1996 = 820454400;
Patrick Venture0b02be92018-08-31 11:55:55 -070046static constexpr auto maxMfgDateValue = 0xFFFFFF; // 3 Byte length
Andres Oportus7ebd2462018-04-09 10:35:21 -070047static constexpr auto secs_per_min = 60;
Patrick Venture0b02be92018-08-31 11:55:55 -070048static constexpr auto secsToMaxMfgdate =
49 secs_from_1970_1996 + secs_per_min * maxMfgDateValue;
Andres Oportus7ebd2462018-04-09 10:35:21 -070050
Oskar Senft8b189412018-12-07 16:13:38 -050051// Minimum size of resulting FRU blob.
52// This is also the theoretical maximum size according to the spec:
53// 8 bytes header + 5 areas at 0xff*8 bytes max each
54// 8 + 5*0xff*8 = 0x27e0
55static constexpr auto fruMinSize = 0x27E0;
56
57// Value to use for padding.
58// Using 0xff to match the default (blank) value in a physical EEPROM.
59static constexpr auto fruPadValue = 0xff;
60
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050061/**
62 * @brief Format Beginning of Individual IPMI FRU Data Section
63 *
64 * @param[in] langCode Language code
65 * @param[in/out] data FRU area data
66 */
67void preFormatProcessing(bool langCode, FruAreaData& data)
68{
Patrick Venture0b02be92018-08-31 11:55:55 -070069 // Add id for version of FRU Info Storage Spec used
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050070 data.emplace_back(specVersion);
71
Patrick Venture0b02be92018-08-31 11:55:55 -070072 // Add Data Size - 0 as a placeholder, can edit after the data is finalized
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050073 data.emplace_back(typeLengthByteNull);
74
75 if (langCode)
76 {
77 data.emplace_back(englishLanguageCode);
78 }
79}
80
81/**
82 * @brief Append checksum of the FRU area data
83 *
84 * @param[in/out] data FRU area data
85 */
86void appendDataChecksum(FruAreaData& data)
87{
88 uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0);
89 // Push the Zero checksum as the last byte of this data
90 // This appears to be a simple summation of all the bytes
91 data.emplace_back(-checksumVal);
92}
93
94/**
95 * @brief Append padding bytes for the FRU area data
96 *
97 * @param[in/out] data FRU area data
98 */
99void padData(FruAreaData& data)
100{
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530101 uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurement;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500102 if (pad)
103 {
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530104 data.resize((data.size() + recordUnitOfMeasurement - pad));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500105 }
106}
107
108/**
109 * @brief Format End of Individual IPMI FRU Data Section
110 *
111 * @param[in/out] fruAreaData FRU area info data
112 */
113void postFormatProcessing(FruAreaData& data)
114{
Patrick Venture0b02be92018-08-31 11:55:55 -0700115 // This area needs to be padded to a multiple of 8 bytes (after checksum)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500116 padData(data);
117
Patrick Venture0b02be92018-08-31 11:55:55 -0700118 // Set size of data info area
119 data.at(areaSizeOffset) =
120 (data.size() + checksumSize) / (recordUnitOfMeasurement);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500121
Patrick Venture0b02be92018-08-31 11:55:55 -0700122 // Finally add area checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500123 appendDataChecksum(data);
124}
125
126/**
Oskar Senft40f59e22018-12-04 22:43:19 -0500127 * @brief Read chassis type property value from inventory and append to the FRU
128 * area data.
129 *
130 * @param[in] propMap map of property values
131 * @param[in,out] data FRU area data to be appended
132 */
133void appendChassisType(const PropertyMap& propMap, FruAreaData& data)
134{
135 uint8_t chassisType = 0; // Not specified
136 auto iter = propMap.find(type);
137 if (iter != propMap.end())
138 {
139 auto value = iter->second;
Oskar Senftcf059392018-12-17 16:26:32 -0500140 try
141 {
142 chassisType = std::stoi(value);
143 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500144 catch (const std::exception& e)
Oskar Senftcf059392018-12-17 16:26:32 -0500145 {
146 log<level::ERR>("Could not parse chassis type",
147 entry("VALUE=%s", value.c_str()),
148 entry("ERROR=%s", e.what()));
149 chassisType = 0;
150 }
Oskar Senft40f59e22018-12-04 22:43:19 -0500151 }
152 data.emplace_back(chassisType);
153}
154
155/**
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500156 * @brief Read property value from inventory and append to the FRU area data
157 *
158 * @param[in] key key to search for in the property inventory data
159 * @param[in] propMap map of property values
160 * @param[in,out] data FRU area data to be appended
161 */
162void appendData(const Property& key, const PropertyMap& propMap,
163 FruAreaData& data)
164{
165 auto iter = propMap.find(key);
166 if (iter != propMap.end())
167 {
168 auto value = iter->second;
Patrick Venture0b02be92018-08-31 11:55:55 -0700169 // If starts with 0x or 0X remove them
170 // ex: 0x123a just take 123a
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500171 if ((value.compare(0, 2, "0x")) == 0 ||
Patrick Venture0b02be92018-08-31 11:55:55 -0700172 (value.compare(0, 2, "0X") == 0))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500173 {
174 value.erase(0, 2);
175 }
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530176
Kirill Pakhomove1160cb2020-03-24 17:08:38 +0300177 // 6 bits for length as per FRU spec v1.0
178 // if length is greater then 63(2^6) bytes then trim the data to 63
Patrick Venture0b02be92018-08-31 11:55:55 -0700179 // bytess.
180 auto valueLength = (value.length() > maxRecordAttributeValue)
181 ? maxRecordAttributeValue
182 : value.length();
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530183 // 2 bits for type
184 // Set the type to ascii
185 uint8_t typeLength = valueLength | ipmi::fru::typeASCII;
186
187 data.emplace_back(typeLength);
Patrick Venture0b02be92018-08-31 11:55:55 -0700188 std::copy(value.begin(), value.begin() + valueLength,
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530189 std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500190 }
191 else
192 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700193 // set 0 size
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500194 data.emplace_back(typeLengthByteNull);
195 }
196}
197
Patrick Venture24c771c2019-07-25 16:34:41 -0700198std::time_t timeStringToRaw(const std::string& input)
199{
200 // TODO: For non-US region timestamps, pass in region information for the
201 // FRU to avoid the month/day swap.
202 // 2017-02-24 - 13:59:00, Tue Nov 20 23:08:00 2018
203 static const std::vector<std::string> patterns = {"%Y-%m-%d - %H:%M:%S",
204 "%a %b %d %H:%M:%S %Y"};
205
206 std::tm time = {};
207
208 for (const auto& pattern : patterns)
209 {
210 std::istringstream timeStream(input);
211 timeStream >> std::get_time(&time, pattern.c_str());
212 if (!timeStream.fail())
213 {
214 break;
215 }
216 }
217
218 return std::mktime(&time);
219}
220
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500221/**
222 * @brief Appends Build Date
223 *
224 * @param[in] propMap map of property values
225 * @param[in/out] data FRU area to add the manfufacture date
226 */
227void appendMfgDate(const PropertyMap& propMap, FruAreaData& data)
228{
Patrick Venture0b02be92018-08-31 11:55:55 -0700229 // MFG Date/Time
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500230 auto iter = propMap.find(buildDate);
Nagaraju Gorugantib898cde2018-07-10 00:47:43 -0500231 if ((iter != propMap.end()) && (iter->second.size() > 0))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500232 {
Patrick Venture24c771c2019-07-25 16:34:41 -0700233 std::time_t raw = timeStringToRaw(iter->second);
Andres Oportus7ebd2462018-04-09 10:35:21 -0700234
235 // From FRU Spec:
236 // "Mfg. Date / Time
237 // Number of minutes from 0:00 hrs 1/1/96.
238 // LSbyte first (little endian)
239 // 00_00_00h = unspecified."
Nagaraju Gorugantib898cde2018-07-10 00:47:43 -0500240 if ((raw >= secs_from_1970_1996) && (raw <= secsToMaxMfgdate))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500241 {
Andres Oportus7ebd2462018-04-09 10:35:21 -0700242 raw -= secs_from_1970_1996;
243 raw /= secs_per_min;
244 uint8_t fru_raw[3];
245 fru_raw[0] = raw & 0xFF;
246 fru_raw[1] = (raw >> 8) & 0xFF;
247 fru_raw[2] = (raw >> 16) & 0xFF;
248 std::copy(fru_raw, fru_raw + 3, std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500249 return;
250 }
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700251 std::fprintf(stderr, "MgfDate invalid date: %u secs since UNIX epoch\n",
252 static_cast<unsigned int>(raw));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500253 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700254 // Blank date
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500255 data.emplace_back(0);
256 data.emplace_back(0);
257 data.emplace_back(0);
258}
259
260/**
261 * @brief Builds a section of the common header
262 *
263 * @param[in] infoAreaSize size of the FRU area to write
264 * @param[in] offset Current offset for data in overall record
265 * @param[in/out] data Common Header section data container
266 */
Patrick Venture0b02be92018-08-31 11:55:55 -0700267void buildCommonHeaderSection(const uint32_t& infoAreaSize, uint16_t& offset,
268 FruAreaData& data)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500269{
Patrick Venture0b02be92018-08-31 11:55:55 -0700270 // Check if data for internal use section populated
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500271 if (infoAreaSize == 0)
272 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700273 // Indicate record not present
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500274 data.emplace_back(recordNotPresent);
275 }
276 else
277 {
Ratan Gupta2f66f002018-01-31 21:26:25 +0530278 // offset should be multiple of 8.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530279 auto remainder = offset % recordUnitOfMeasurement;
Ratan Gupta2f66f002018-01-31 21:26:25 +0530280 // add the padding bytes in the offset so that offset
281 // will be multiple of 8 byte.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530282 offset += (remainder > 0) ? recordUnitOfMeasurement - remainder : 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700283 // Place data to define offset to area data section
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530284 data.emplace_back(offset / recordUnitOfMeasurement);
Ratan Gupta2f66f002018-01-31 21:26:25 +0530285
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500286 offset += infoAreaSize;
287 }
288}
289
290/**
291 * @brief Builds the Chassis info area data section
292 *
293 * @param[in] propMap map of properties for chassis info area
294 * @return FruAreaData container with chassis info area
295 */
296FruAreaData buildChassisInfoArea(const PropertyMap& propMap)
297{
298 FruAreaData fruAreaData;
299 if (!propMap.empty())
300 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700301 // Set formatting data that goes at the beginning of the record
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500302 preFormatProcessing(false, fruAreaData);
303
Patrick Venture0b02be92018-08-31 11:55:55 -0700304 // chassis type
Oskar Senft40f59e22018-12-04 22:43:19 -0500305 appendChassisType(propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500306
Patrick Venture0b02be92018-08-31 11:55:55 -0700307 // Chasiss part number, in config.yaml it is configured as model
William A. Kennington IIIecd9f372020-05-31 15:52:09 -0700308 appendData(modelNumber, propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500309
Patrick Venture0b02be92018-08-31 11:55:55 -0700310 // Board serial number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500311 appendData(serialNumber, propMap, fruAreaData);
312
Patrick Venture0b02be92018-08-31 11:55:55 -0700313 // Indicate End of Custom Fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500314 fruAreaData.emplace_back(endOfCustomFields);
315
Patrick Venture0b02be92018-08-31 11:55:55 -0700316 // Complete record data formatting
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500317 postFormatProcessing(fruAreaData);
318 }
319 return fruAreaData;
320}
321
322/**
323 * @brief Builds the Board info area data section
324 *
325 * @param[in] propMap map of properties for board info area
326 * @return FruAreaData container with board info area
327 */
328FruAreaData buildBoardInfoArea(const PropertyMap& propMap)
329{
330 FruAreaData fruAreaData;
331 if (!propMap.empty())
332 {
333 preFormatProcessing(true, fruAreaData);
334
Patrick Venture0b02be92018-08-31 11:55:55 -0700335 // Manufacturing date
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500336 appendMfgDate(propMap, fruAreaData);
337
Patrick Venture0b02be92018-08-31 11:55:55 -0700338 // manufacturer
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500339 appendData(manufacturer, propMap, fruAreaData);
340
Patrick Venture0b02be92018-08-31 11:55:55 -0700341 // Product name/Pretty name
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500342 appendData(prettyName, propMap, fruAreaData);
343
Patrick Venture0b02be92018-08-31 11:55:55 -0700344 // Board serial number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500345 appendData(serialNumber, propMap, fruAreaData);
346
Patrick Venture0b02be92018-08-31 11:55:55 -0700347 // Board part number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500348 appendData(partNumber, propMap, fruAreaData);
349
Patrick Venture0b02be92018-08-31 11:55:55 -0700350 // FRU File ID - Empty
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500351 fruAreaData.emplace_back(typeLengthByteNull);
352
353 // Empty FRU File ID bytes
354 fruAreaData.emplace_back(recordNotPresent);
355
Patrick Venture0b02be92018-08-31 11:55:55 -0700356 // End of custom fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500357 fruAreaData.emplace_back(endOfCustomFields);
358
359 postFormatProcessing(fruAreaData);
360 }
361 return fruAreaData;
362}
363
364/**
365 * @brief Builds the Product info area data section
366 *
367 * @param[in] propMap map of FRU properties for Board info area
368 * @return FruAreaData container with product info area data
369 */
370FruAreaData buildProductInfoArea(const PropertyMap& propMap)
371{
372 FruAreaData fruAreaData;
373 if (!propMap.empty())
374 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700375 // Set formatting data that goes at the beginning of the record
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500376 preFormatProcessing(true, fruAreaData);
377
Patrick Venture0b02be92018-08-31 11:55:55 -0700378 // manufacturer
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500379 appendData(manufacturer, propMap, fruAreaData);
380
Patrick Venture0b02be92018-08-31 11:55:55 -0700381 // Product name/Pretty name
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500382 appendData(prettyName, propMap, fruAreaData);
383
Patrick Venture0b02be92018-08-31 11:55:55 -0700384 // Product part/model number
William A. Kennington IIIecd9f372020-05-31 15:52:09 -0700385 appendData(modelNumber, propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500386
Patrick Venture0b02be92018-08-31 11:55:55 -0700387 // Product version
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500388 appendData(version, propMap, fruAreaData);
389
Patrick Venture0b02be92018-08-31 11:55:55 -0700390 // Serial Number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500391 appendData(serialNumber, propMap, fruAreaData);
392
Patrick Venture0b02be92018-08-31 11:55:55 -0700393 // Add Asset Tag
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500394 fruAreaData.emplace_back(recordNotPresent);
395
Patrick Venture0b02be92018-08-31 11:55:55 -0700396 // FRU File ID - Empty
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500397 fruAreaData.emplace_back(typeLengthByteNull);
398
399 // Empty FRU File ID bytes
400 fruAreaData.emplace_back(recordNotPresent);
401
Patrick Venture0b02be92018-08-31 11:55:55 -0700402 // End of custom fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500403 fruAreaData.emplace_back(endOfCustomFields);
404
405 postFormatProcessing(fruAreaData);
406 }
407 return fruAreaData;
408}
409
410FruAreaData buildFruAreaData(const FruInventoryData& inventory)
411{
Ratan Gupta2f66f002018-01-31 21:26:25 +0530412 FruAreaData combFruArea{};
Patrick Venture0b02be92018-08-31 11:55:55 -0700413 // Now build common header with data for this FRU Inv Record
414 // Use this variable to increment size of header as we go along to determine
415 // offset for the subsequent area offsets
Ratan Gupta2f66f002018-01-31 21:26:25 +0530416 uint16_t curDataOffset = commonHeaderFormatSize;
Patrick Venture0b02be92018-08-31 11:55:55 -0700417 // First byte is id for version of FRU Info Storage Spec used
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500418 combFruArea.emplace_back(specVersion);
419
Patrick Venture0b02be92018-08-31 11:55:55 -0700420 // 2nd byte is offset to internal use data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500421 combFruArea.emplace_back(recordNotPresent);
422
Patrick Venture0b02be92018-08-31 11:55:55 -0700423 // 3rd byte is offset to chassis data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500424 FruAreaData chassisArea;
425 auto chassisIt = inventory.find(chassis);
426 if (chassisIt != inventory.end())
427 {
Patrick Venture2e529ee2020-07-04 09:57:05 -0700428 chassisArea = buildChassisInfoArea(chassisIt->second);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500429 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530430 // update the offset to chassis data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500431 buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea);
432
Patrick Venture0b02be92018-08-31 11:55:55 -0700433 // 4th byte is offset to board data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500434 FruAreaData boardArea;
435 auto boardIt = inventory.find(board);
436 if (boardIt != inventory.end())
437 {
Patrick Venture2e529ee2020-07-04 09:57:05 -0700438 boardArea = buildBoardInfoArea(boardIt->second);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500439 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530440 // update the offset to the board data.
441 buildCommonHeaderSection(boardArea.size(), curDataOffset, combFruArea);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500442
Patrick Venture0b02be92018-08-31 11:55:55 -0700443 // 5th byte is offset to product data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500444 FruAreaData prodArea;
445 auto prodIt = inventory.find(product);
446 if (prodIt != inventory.end())
447 {
Patrick Venture2e529ee2020-07-04 09:57:05 -0700448 prodArea = buildProductInfoArea(prodIt->second);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500449 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530450 // update the offset to the product data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500451 buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea);
452
Patrick Venture0b02be92018-08-31 11:55:55 -0700453 // 6th byte is offset to multirecord data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500454 combFruArea.emplace_back(recordNotPresent);
455
Patrick Venture0b02be92018-08-31 11:55:55 -0700456 // 7th byte is PAD
Ratan Gupta2f66f002018-01-31 21:26:25 +0530457 combFruArea.emplace_back(recordNotPresent);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500458
Patrick Venture0b02be92018-08-31 11:55:55 -0700459 // 8th (Final byte of Header Format) is the checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500460 appendDataChecksum(combFruArea);
461
Patrick Venture0b02be92018-08-31 11:55:55 -0700462 // Combine everything into one full IPMI FRU specification Record
463 // add chassis use area data
464 combFruArea.insert(combFruArea.end(), chassisArea.begin(),
465 chassisArea.end());
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500466
Patrick Venture0b02be92018-08-31 11:55:55 -0700467 // add board area data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500468 combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end());
469
Patrick Venture0b02be92018-08-31 11:55:55 -0700470 // add product use area data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500471 combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end());
472
Oskar Senft8b189412018-12-07 16:13:38 -0500473 // If area is smaller than the minimum size, pad it. This enables ipmitool
474 // to update the FRU blob with values longer than the original payload.
475 if (combFruArea.size() < fruMinSize)
476 {
477 combFruArea.resize(fruMinSize, fruPadValue);
478 }
479
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500480 return combFruArea;
481}
482
Patrick Venture0b02be92018-08-31 11:55:55 -0700483} // namespace fru
484} // namespace ipmi