blob: 3cbfb0921a319bf757408893b9aa5891ca8d25fd [file] [log] [blame]
Patrick Venture0b02be92018-08-31 11:55:55 -07001#include "ipmi_fru_info_area.hpp"
2
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05003#include <phosphor-logging/elog.hpp>
George Liu669e88e2024-07-19 09:24:30 +08004#include <phosphor-logging/lg2.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05005
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05006#include <algorithm>
Patrick Venture0b02be92018-08-31 11:55:55 -07007#include <ctime>
Patrick Venture24c771c2019-07-25 16:34:41 -07008#include <iomanip>
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05009#include <map>
10#include <numeric>
Patrick Venture24c771c2019-07-25 16:34:41 -070011#include <sstream>
Patrick Ventureb51bf9c2018-09-10 15:53:14 -070012
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050013namespace ipmi
14{
15namespace fru
16{
17using namespace phosphor::logging;
18
Patrick Venture0b02be92018-08-31 11:55:55 -070019// Property variables
Patrick Venture45e93cb2019-07-25 15:03:19 -070020static constexpr auto partNumber = "Part Number";
21static constexpr auto serialNumber = "Serial Number";
Patrick Venture0b02be92018-08-31 11:55:55 -070022static constexpr auto manufacturer = "Manufacturer";
Patrick Venture45e93cb2019-07-25 15:03:19 -070023static constexpr auto buildDate = "Mfg Date";
William A. Kennington IIIecd9f372020-05-31 15:52:09 -070024static constexpr auto modelNumber = "Model Number";
Patrick Venture45e93cb2019-07-25 15:03:19 -070025static constexpr auto prettyName = "Name";
Patrick Venture0b02be92018-08-31 11:55:55 -070026static constexpr auto version = "Version";
Oskar Senft40f59e22018-12-04 22:43:19 -050027static constexpr auto type = "Type";
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050028
Patrick Venture0b02be92018-08-31 11:55:55 -070029// Board info areas
30static constexpr auto board = "Board";
31static constexpr auto chassis = "Chassis";
32static constexpr auto product = "Product";
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050033
Patrick Venture0b02be92018-08-31 11:55:55 -070034static constexpr auto specVersion = 0x1;
35static constexpr auto recordUnitOfMeasurement = 0x8; // size in bytes
36static constexpr auto checksumSize = 0x1; // size in bytes
37static constexpr auto recordNotPresent = 0x0;
38static constexpr auto englishLanguageCode = 0x0;
39static constexpr auto typeLengthByteNull = 0x0;
40static constexpr auto endOfCustomFields = 0xC1;
41static constexpr auto commonHeaderFormatSize = 0x8; // size in bytes
42static constexpr auto manufacturingDateSize = 0x3;
43static constexpr auto areaSizeOffset = 0x1;
44static constexpr uint8_t typeASCII = 0xC0;
Kirill Pakhomove1160cb2020-03-24 17:08:38 +030045static constexpr auto maxRecordAttributeValue = 0x3F;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050046
Andres Oportus7ebd2462018-04-09 10:35:21 -070047static constexpr auto secs_from_1970_1996 = 820454400;
Patrick Venture0b02be92018-08-31 11:55:55 -070048static constexpr auto maxMfgDateValue = 0xFFFFFF; // 3 Byte length
Andres Oportus7ebd2462018-04-09 10:35:21 -070049static constexpr auto secs_per_min = 60;
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050050static constexpr auto secsToMaxMfgdate = secs_from_1970_1996 +
51 secs_per_min * maxMfgDateValue;
Andres Oportus7ebd2462018-04-09 10:35:21 -070052
Oskar Senft8b189412018-12-07 16:13:38 -050053// Minimum size of resulting FRU blob.
54// This is also the theoretical maximum size according to the spec:
55// 8 bytes header + 5 areas at 0xff*8 bytes max each
56// 8 + 5*0xff*8 = 0x27e0
57static constexpr auto fruMinSize = 0x27E0;
58
59// Value to use for padding.
60// Using 0xff to match the default (blank) value in a physical EEPROM.
61static constexpr auto fruPadValue = 0xff;
62
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050063/**
64 * @brief Format Beginning of Individual IPMI FRU Data Section
65 *
66 * @param[in] langCode Language code
67 * @param[in/out] data FRU area data
68 */
69void preFormatProcessing(bool langCode, FruAreaData& data)
70{
Patrick Venture0b02be92018-08-31 11:55:55 -070071 // Add id for version of FRU Info Storage Spec used
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050072 data.emplace_back(specVersion);
73
Patrick Venture0b02be92018-08-31 11:55:55 -070074 // Add Data Size - 0 as a placeholder, can edit after the data is finalized
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050075 data.emplace_back(typeLengthByteNull);
76
77 if (langCode)
78 {
79 data.emplace_back(englishLanguageCode);
80 }
81}
82
83/**
84 * @brief Append checksum of the FRU area data
85 *
86 * @param[in/out] data FRU area data
87 */
88void appendDataChecksum(FruAreaData& data)
89{
90 uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0);
91 // Push the Zero checksum as the last byte of this data
92 // This appears to be a simple summation of all the bytes
93 data.emplace_back(-checksumVal);
94}
95
96/**
97 * @brief Append padding bytes for the FRU area data
98 *
99 * @param[in/out] data FRU area data
100 */
101void padData(FruAreaData& data)
102{
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530103 uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurement;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500104 if (pad)
105 {
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530106 data.resize((data.size() + recordUnitOfMeasurement - pad));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500107 }
108}
109
110/**
111 * @brief Format End of Individual IPMI FRU Data Section
112 *
113 * @param[in/out] fruAreaData FRU area info data
114 */
115void postFormatProcessing(FruAreaData& data)
116{
Patrick Venture0b02be92018-08-31 11:55:55 -0700117 // This area needs to be padded to a multiple of 8 bytes (after checksum)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500118 padData(data);
119
Patrick Venture0b02be92018-08-31 11:55:55 -0700120 // Set size of data info area
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500121 data.at(areaSizeOffset) = (data.size() + checksumSize) /
122 (recordUnitOfMeasurement);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500123
Patrick Venture0b02be92018-08-31 11:55:55 -0700124 // Finally add area checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500125 appendDataChecksum(data);
126}
127
128/**
Oskar Senft40f59e22018-12-04 22:43:19 -0500129 * @brief Read chassis type property value from inventory and append to the FRU
130 * area data.
131 *
132 * @param[in] propMap map of property values
133 * @param[in,out] data FRU area data to be appended
134 */
135void appendChassisType(const PropertyMap& propMap, FruAreaData& data)
136{
137 uint8_t chassisType = 0; // Not specified
138 auto iter = propMap.find(type);
139 if (iter != propMap.end())
140 {
141 auto value = iter->second;
Oskar Senftcf059392018-12-17 16:26:32 -0500142 try
143 {
144 chassisType = std::stoi(value);
145 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500146 catch (const std::exception& e)
Oskar Senftcf059392018-12-17 16:26:32 -0500147 {
George Liu669e88e2024-07-19 09:24:30 +0800148 lg2::error("Could not parse chassis type, value: {VALUE}, "
149 "error: {ERROR}",
150 "VALUE", value, "ERROR", e);
Oskar Senftcf059392018-12-17 16:26:32 -0500151 chassisType = 0;
152 }
Oskar Senft40f59e22018-12-04 22:43:19 -0500153 }
154 data.emplace_back(chassisType);
155}
156
157/**
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500158 * @brief Read property value from inventory and append to the FRU area data
159 *
160 * @param[in] key key to search for in the property inventory data
161 * @param[in] propMap map of property values
162 * @param[in,out] data FRU area data to be appended
163 */
164void appendData(const Property& key, const PropertyMap& propMap,
165 FruAreaData& data)
166{
167 auto iter = propMap.find(key);
168 if (iter != propMap.end())
169 {
170 auto value = iter->second;
Patrick Venture0b02be92018-08-31 11:55:55 -0700171 // If starts with 0x or 0X remove them
172 // ex: 0x123a just take 123a
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500173 if ((value.compare(0, 2, "0x")) == 0 ||
Patrick Venture0b02be92018-08-31 11:55:55 -0700174 (value.compare(0, 2, "0X") == 0))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500175 {
176 value.erase(0, 2);
177 }
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530178
Kirill Pakhomove1160cb2020-03-24 17:08:38 +0300179 // 6 bits for length as per FRU spec v1.0
180 // if length is greater then 63(2^6) bytes then trim the data to 63
Patrick Venture0b02be92018-08-31 11:55:55 -0700181 // bytess.
182 auto valueLength = (value.length() > maxRecordAttributeValue)
183 ? maxRecordAttributeValue
184 : value.length();
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530185 // 2 bits for type
186 // Set the type to ascii
187 uint8_t typeLength = valueLength | ipmi::fru::typeASCII;
188
189 data.emplace_back(typeLength);
Patrick Venture0b02be92018-08-31 11:55:55 -0700190 std::copy(value.begin(), value.begin() + valueLength,
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530191 std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500192 }
193 else
194 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700195 // set 0 size
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500196 data.emplace_back(typeLengthByteNull);
197 }
198}
199
Patrick Venture24c771c2019-07-25 16:34:41 -0700200std::time_t timeStringToRaw(const std::string& input)
201{
202 // TODO: For non-US region timestamps, pass in region information for the
203 // FRU to avoid the month/day swap.
204 // 2017-02-24 - 13:59:00, Tue Nov 20 23:08:00 2018
205 static const std::vector<std::string> patterns = {"%Y-%m-%d - %H:%M:%S",
206 "%a %b %d %H:%M:%S %Y"};
207
208 std::tm time = {};
209
210 for (const auto& pattern : patterns)
211 {
212 std::istringstream timeStream(input);
213 timeStream >> std::get_time(&time, pattern.c_str());
214 if (!timeStream.fail())
215 {
216 break;
217 }
218 }
219
Willy Tub2411302022-11-03 08:49:51 +0000220 return timegm(&time);
Patrick Venture24c771c2019-07-25 16:34:41 -0700221}
222
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500223/**
224 * @brief Appends Build Date
225 *
226 * @param[in] propMap map of property values
227 * @param[in/out] data FRU area to add the manfufacture date
228 */
229void appendMfgDate(const PropertyMap& propMap, FruAreaData& data)
230{
Patrick Venture0b02be92018-08-31 11:55:55 -0700231 // MFG Date/Time
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500232 auto iter = propMap.find(buildDate);
Nagaraju Gorugantib898cde2018-07-10 00:47:43 -0500233 if ((iter != propMap.end()) && (iter->second.size() > 0))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500234 {
Patrick Venture24c771c2019-07-25 16:34:41 -0700235 std::time_t raw = timeStringToRaw(iter->second);
Andres Oportus7ebd2462018-04-09 10:35:21 -0700236
237 // From FRU Spec:
238 // "Mfg. Date / Time
239 // Number of minutes from 0:00 hrs 1/1/96.
240 // LSbyte first (little endian)
241 // 00_00_00h = unspecified."
Nagaraju Gorugantib898cde2018-07-10 00:47:43 -0500242 if ((raw >= secs_from_1970_1996) && (raw <= secsToMaxMfgdate))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500243 {
Andres Oportus7ebd2462018-04-09 10:35:21 -0700244 raw -= secs_from_1970_1996;
245 raw /= secs_per_min;
246 uint8_t fru_raw[3];
247 fru_raw[0] = raw & 0xFF;
248 fru_raw[1] = (raw >> 8) & 0xFF;
249 fru_raw[2] = (raw >> 16) & 0xFF;
250 std::copy(fru_raw, fru_raw + 3, std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500251 return;
252 }
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700253 std::fprintf(stderr, "MgfDate invalid date: %u secs since UNIX epoch\n",
254 static_cast<unsigned int>(raw));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500255 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700256 // Blank date
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500257 data.emplace_back(0);
258 data.emplace_back(0);
259 data.emplace_back(0);
260}
261
262/**
263 * @brief Builds a section of the common header
264 *
265 * @param[in] infoAreaSize size of the FRU area to write
266 * @param[in] offset Current offset for data in overall record
267 * @param[in/out] data Common Header section data container
268 */
Patrick Venture0b02be92018-08-31 11:55:55 -0700269void buildCommonHeaderSection(const uint32_t& infoAreaSize, uint16_t& offset,
270 FruAreaData& data)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500271{
Patrick Venture0b02be92018-08-31 11:55:55 -0700272 // Check if data for internal use section populated
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500273 if (infoAreaSize == 0)
274 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700275 // Indicate record not present
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500276 data.emplace_back(recordNotPresent);
277 }
278 else
279 {
Ratan Gupta2f66f002018-01-31 21:26:25 +0530280 // offset should be multiple of 8.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530281 auto remainder = offset % recordUnitOfMeasurement;
Ratan Gupta2f66f002018-01-31 21:26:25 +0530282 // add the padding bytes in the offset so that offset
283 // will be multiple of 8 byte.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530284 offset += (remainder > 0) ? recordUnitOfMeasurement - remainder : 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700285 // Place data to define offset to area data section
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530286 data.emplace_back(offset / recordUnitOfMeasurement);
Ratan Gupta2f66f002018-01-31 21:26:25 +0530287
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500288 offset += infoAreaSize;
289 }
290}
291
292/**
293 * @brief Builds the Chassis info area data section
294 *
295 * @param[in] propMap map of properties for chassis info area
296 * @return FruAreaData container with chassis info area
297 */
298FruAreaData buildChassisInfoArea(const PropertyMap& propMap)
299{
300 FruAreaData fruAreaData;
301 if (!propMap.empty())
302 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700303 // Set formatting data that goes at the beginning of the record
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500304 preFormatProcessing(false, fruAreaData);
305
Patrick Venture0b02be92018-08-31 11:55:55 -0700306 // chassis type
Oskar Senft40f59e22018-12-04 22:43:19 -0500307 appendChassisType(propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500308
Patrick Venture0b02be92018-08-31 11:55:55 -0700309 // Chasiss part number, in config.yaml it is configured as model
William A. Kennington IIIecd9f372020-05-31 15:52:09 -0700310 appendData(modelNumber, propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500311
Patrick Venture0b02be92018-08-31 11:55:55 -0700312 // Board serial number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500313 appendData(serialNumber, propMap, fruAreaData);
314
Patrick Venture0b02be92018-08-31 11:55:55 -0700315 // Indicate End of Custom Fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500316 fruAreaData.emplace_back(endOfCustomFields);
317
Patrick Venture0b02be92018-08-31 11:55:55 -0700318 // Complete record data formatting
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500319 postFormatProcessing(fruAreaData);
320 }
321 return fruAreaData;
322}
323
324/**
325 * @brief Builds the Board info area data section
326 *
327 * @param[in] propMap map of properties for board info area
328 * @return FruAreaData container with board info area
329 */
330FruAreaData buildBoardInfoArea(const PropertyMap& propMap)
331{
332 FruAreaData fruAreaData;
333 if (!propMap.empty())
334 {
335 preFormatProcessing(true, fruAreaData);
336
Patrick Venture0b02be92018-08-31 11:55:55 -0700337 // Manufacturing date
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500338 appendMfgDate(propMap, fruAreaData);
339
Patrick Venture0b02be92018-08-31 11:55:55 -0700340 // manufacturer
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500341 appendData(manufacturer, propMap, fruAreaData);
342
Patrick Venture0b02be92018-08-31 11:55:55 -0700343 // Product name/Pretty name
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500344 appendData(prettyName, propMap, fruAreaData);
345
Patrick Venture0b02be92018-08-31 11:55:55 -0700346 // Board serial number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500347 appendData(serialNumber, propMap, fruAreaData);
348
Patrick Venture0b02be92018-08-31 11:55:55 -0700349 // Board part number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500350 appendData(partNumber, propMap, fruAreaData);
351
Patrick Venture0b02be92018-08-31 11:55:55 -0700352 // FRU File ID - Empty
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500353 fruAreaData.emplace_back(typeLengthByteNull);
354
355 // Empty FRU File ID bytes
356 fruAreaData.emplace_back(recordNotPresent);
357
Patrick Venture0b02be92018-08-31 11:55:55 -0700358 // End of custom fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500359 fruAreaData.emplace_back(endOfCustomFields);
360
361 postFormatProcessing(fruAreaData);
362 }
363 return fruAreaData;
364}
365
366/**
367 * @brief Builds the Product info area data section
368 *
369 * @param[in] propMap map of FRU properties for Board info area
370 * @return FruAreaData container with product info area data
371 */
372FruAreaData buildProductInfoArea(const PropertyMap& propMap)
373{
374 FruAreaData fruAreaData;
375 if (!propMap.empty())
376 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700377 // Set formatting data that goes at the beginning of the record
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500378 preFormatProcessing(true, fruAreaData);
379
Patrick Venture0b02be92018-08-31 11:55:55 -0700380 // manufacturer
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500381 appendData(manufacturer, propMap, fruAreaData);
382
Patrick Venture0b02be92018-08-31 11:55:55 -0700383 // Product name/Pretty name
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500384 appendData(prettyName, propMap, fruAreaData);
385
Patrick Venture0b02be92018-08-31 11:55:55 -0700386 // Product part/model number
William A. Kennington IIIecd9f372020-05-31 15:52:09 -0700387 appendData(modelNumber, propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500388
Patrick Venture0b02be92018-08-31 11:55:55 -0700389 // Product version
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500390 appendData(version, propMap, fruAreaData);
391
Patrick Venture0b02be92018-08-31 11:55:55 -0700392 // Serial Number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500393 appendData(serialNumber, propMap, fruAreaData);
394
Patrick Venture0b02be92018-08-31 11:55:55 -0700395 // Add Asset Tag
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500396 fruAreaData.emplace_back(recordNotPresent);
397
Patrick Venture0b02be92018-08-31 11:55:55 -0700398 // FRU File ID - Empty
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500399 fruAreaData.emplace_back(typeLengthByteNull);
400
401 // Empty FRU File ID bytes
402 fruAreaData.emplace_back(recordNotPresent);
403
Patrick Venture0b02be92018-08-31 11:55:55 -0700404 // End of custom fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500405 fruAreaData.emplace_back(endOfCustomFields);
406
407 postFormatProcessing(fruAreaData);
408 }
409 return fruAreaData;
410}
411
412FruAreaData buildFruAreaData(const FruInventoryData& inventory)
413{
Ratan Gupta2f66f002018-01-31 21:26:25 +0530414 FruAreaData combFruArea{};
Patrick Venture0b02be92018-08-31 11:55:55 -0700415 // Now build common header with data for this FRU Inv Record
416 // Use this variable to increment size of header as we go along to determine
417 // offset for the subsequent area offsets
Ratan Gupta2f66f002018-01-31 21:26:25 +0530418 uint16_t curDataOffset = commonHeaderFormatSize;
Patrick Venture0b02be92018-08-31 11:55:55 -0700419 // First byte is id for version of FRU Info Storage Spec used
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500420 combFruArea.emplace_back(specVersion);
421
Patrick Venture0b02be92018-08-31 11:55:55 -0700422 // 2nd byte is offset to internal use data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500423 combFruArea.emplace_back(recordNotPresent);
424
Patrick Venture0b02be92018-08-31 11:55:55 -0700425 // 3rd byte is offset to chassis data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500426 FruAreaData chassisArea;
427 auto chassisIt = inventory.find(chassis);
428 if (chassisIt != inventory.end())
429 {
Patrick Venture2e529ee2020-07-04 09:57:05 -0700430 chassisArea = buildChassisInfoArea(chassisIt->second);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500431 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530432 // update the offset to chassis data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500433 buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea);
434
Patrick Venture0b02be92018-08-31 11:55:55 -0700435 // 4th byte is offset to board data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500436 FruAreaData boardArea;
437 auto boardIt = inventory.find(board);
438 if (boardIt != inventory.end())
439 {
Patrick Venture2e529ee2020-07-04 09:57:05 -0700440 boardArea = buildBoardInfoArea(boardIt->second);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500441 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530442 // update the offset to the board data.
443 buildCommonHeaderSection(boardArea.size(), curDataOffset, combFruArea);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500444
Patrick Venture0b02be92018-08-31 11:55:55 -0700445 // 5th byte is offset to product data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500446 FruAreaData prodArea;
447 auto prodIt = inventory.find(product);
448 if (prodIt != inventory.end())
449 {
Patrick Venture2e529ee2020-07-04 09:57:05 -0700450 prodArea = buildProductInfoArea(prodIt->second);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500451 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530452 // update the offset to the product data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500453 buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea);
454
Patrick Venture0b02be92018-08-31 11:55:55 -0700455 // 6th byte is offset to multirecord data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500456 combFruArea.emplace_back(recordNotPresent);
457
Patrick Venture0b02be92018-08-31 11:55:55 -0700458 // 7th byte is PAD
Ratan Gupta2f66f002018-01-31 21:26:25 +0530459 combFruArea.emplace_back(recordNotPresent);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500460
Patrick Venture0b02be92018-08-31 11:55:55 -0700461 // 8th (Final byte of Header Format) is the checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500462 appendDataChecksum(combFruArea);
463
Patrick Venture0b02be92018-08-31 11:55:55 -0700464 // Combine everything into one full IPMI FRU specification Record
465 // add chassis use area data
466 combFruArea.insert(combFruArea.end(), chassisArea.begin(),
467 chassisArea.end());
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500468
Patrick Venture0b02be92018-08-31 11:55:55 -0700469 // add board area data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500470 combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end());
471
Patrick Venture0b02be92018-08-31 11:55:55 -0700472 // add product use area data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500473 combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end());
474
Oskar Senft8b189412018-12-07 16:13:38 -0500475 // If area is smaller than the minimum size, pad it. This enables ipmitool
476 // to update the FRU blob with values longer than the original payload.
477 if (combFruArea.size() < fruMinSize)
478 {
479 combFruArea.resize(fruMinSize, fruPadValue);
480 }
481
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500482 return combFruArea;
483}
484
Patrick Venture0b02be92018-08-31 11:55:55 -0700485} // namespace fru
486} // namespace ipmi