blob: ec3f80dd3a906b21f14266b2a0da5c47052b8258 [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>
4
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05005#include <algorithm>
Patrick Venture0b02be92018-08-31 11:55:55 -07006#include <ctime>
Patrick Venture24c771c2019-07-25 16:34:41 -07007#include <iomanip>
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05008#include <map>
9#include <numeric>
Patrick Venture24c771c2019-07-25 16:34:41 -070010#include <sstream>
Patrick Ventureb51bf9c2018-09-10 15:53:14 -070011
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050012namespace ipmi
13{
14namespace fru
15{
16using namespace phosphor::logging;
17
Patrick Venture0b02be92018-08-31 11:55:55 -070018// Property variables
Patrick Venture45e93cb2019-07-25 15:03:19 -070019static constexpr auto partNumber = "Part Number";
20static constexpr auto serialNumber = "Serial Number";
Patrick Venture0b02be92018-08-31 11:55:55 -070021static constexpr auto manufacturer = "Manufacturer";
Patrick Venture45e93cb2019-07-25 15:03:19 -070022static constexpr auto buildDate = "Mfg Date";
William A. Kennington IIIecd9f372020-05-31 15:52:09 -070023static constexpr auto modelNumber = "Model Number";
Patrick Venture45e93cb2019-07-25 15:03:19 -070024static constexpr auto prettyName = "Name";
Patrick Venture0b02be92018-08-31 11:55:55 -070025static constexpr auto version = "Version";
Oskar Senft40f59e22018-12-04 22:43:19 -050026static constexpr auto type = "Type";
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050027
Patrick Venture0b02be92018-08-31 11:55:55 -070028// Board info areas
29static constexpr auto board = "Board";
30static constexpr auto chassis = "Chassis";
31static constexpr auto product = "Product";
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050032
Patrick Venture0b02be92018-08-31 11:55:55 -070033static constexpr auto specVersion = 0x1;
34static constexpr auto recordUnitOfMeasurement = 0x8; // size in bytes
35static constexpr auto checksumSize = 0x1; // size in bytes
36static constexpr auto recordNotPresent = 0x0;
37static constexpr auto englishLanguageCode = 0x0;
38static constexpr auto typeLengthByteNull = 0x0;
39static constexpr auto endOfCustomFields = 0xC1;
40static constexpr auto commonHeaderFormatSize = 0x8; // size in bytes
41static constexpr auto manufacturingDateSize = 0x3;
42static constexpr auto areaSizeOffset = 0x1;
43static constexpr uint8_t typeASCII = 0xC0;
Kirill Pakhomove1160cb2020-03-24 17:08:38 +030044static constexpr auto maxRecordAttributeValue = 0x3F;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050045
Andres Oportus7ebd2462018-04-09 10:35:21 -070046static constexpr auto secs_from_1970_1996 = 820454400;
Patrick Venture0b02be92018-08-31 11:55:55 -070047static constexpr auto maxMfgDateValue = 0xFFFFFF; // 3 Byte length
Andres Oportus7ebd2462018-04-09 10:35:21 -070048static constexpr auto secs_per_min = 60;
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050049static constexpr auto secsToMaxMfgdate = secs_from_1970_1996 +
50 secs_per_min * maxMfgDateValue;
Andres Oportus7ebd2462018-04-09 10:35:21 -070051
Oskar Senft8b189412018-12-07 16:13:38 -050052// Minimum size of resulting FRU blob.
53// This is also the theoretical maximum size according to the spec:
54// 8 bytes header + 5 areas at 0xff*8 bytes max each
55// 8 + 5*0xff*8 = 0x27e0
56static constexpr auto fruMinSize = 0x27E0;
57
58// Value to use for padding.
59// Using 0xff to match the default (blank) value in a physical EEPROM.
60static constexpr auto fruPadValue = 0xff;
61
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050062/**
63 * @brief Format Beginning of Individual IPMI FRU Data Section
64 *
65 * @param[in] langCode Language code
66 * @param[in/out] data FRU area data
67 */
68void preFormatProcessing(bool langCode, FruAreaData& data)
69{
Patrick Venture0b02be92018-08-31 11:55:55 -070070 // Add id for version of FRU Info Storage Spec used
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050071 data.emplace_back(specVersion);
72
Patrick Venture0b02be92018-08-31 11:55:55 -070073 // Add Data Size - 0 as a placeholder, can edit after the data is finalized
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050074 data.emplace_back(typeLengthByteNull);
75
76 if (langCode)
77 {
78 data.emplace_back(englishLanguageCode);
79 }
80}
81
82/**
83 * @brief Append checksum of the FRU area data
84 *
85 * @param[in/out] data FRU area data
86 */
87void appendDataChecksum(FruAreaData& data)
88{
89 uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0);
90 // Push the Zero checksum as the last byte of this data
91 // This appears to be a simple summation of all the bytes
92 data.emplace_back(-checksumVal);
93}
94
95/**
96 * @brief Append padding bytes for the FRU area data
97 *
98 * @param[in/out] data FRU area data
99 */
100void padData(FruAreaData& data)
101{
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530102 uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurement;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500103 if (pad)
104 {
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530105 data.resize((data.size() + recordUnitOfMeasurement - pad));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500106 }
107}
108
109/**
110 * @brief Format End of Individual IPMI FRU Data Section
111 *
112 * @param[in/out] fruAreaData FRU area info data
113 */
114void postFormatProcessing(FruAreaData& data)
115{
Patrick Venture0b02be92018-08-31 11:55:55 -0700116 // This area needs to be padded to a multiple of 8 bytes (after checksum)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500117 padData(data);
118
Patrick Venture0b02be92018-08-31 11:55:55 -0700119 // Set size of data info area
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500120 data.at(areaSizeOffset) = (data.size() + checksumSize) /
121 (recordUnitOfMeasurement);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500122
Patrick Venture0b02be92018-08-31 11:55:55 -0700123 // Finally add area checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500124 appendDataChecksum(data);
125}
126
127/**
Oskar Senft40f59e22018-12-04 22:43:19 -0500128 * @brief Read chassis type property value from inventory and append to the FRU
129 * area data.
130 *
131 * @param[in] propMap map of property values
132 * @param[in,out] data FRU area data to be appended
133 */
134void appendChassisType(const PropertyMap& propMap, FruAreaData& data)
135{
136 uint8_t chassisType = 0; // Not specified
137 auto iter = propMap.find(type);
138 if (iter != propMap.end())
139 {
140 auto value = iter->second;
Oskar Senftcf059392018-12-17 16:26:32 -0500141 try
142 {
143 chassisType = std::stoi(value);
144 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500145 catch (const std::exception& e)
Oskar Senftcf059392018-12-17 16:26:32 -0500146 {
147 log<level::ERR>("Could not parse chassis type",
148 entry("VALUE=%s", value.c_str()),
149 entry("ERROR=%s", e.what()));
150 chassisType = 0;
151 }
Oskar Senft40f59e22018-12-04 22:43:19 -0500152 }
153 data.emplace_back(chassisType);
154}
155
156/**
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500157 * @brief Read property value from inventory and append to the FRU area data
158 *
159 * @param[in] key key to search for in the property inventory data
160 * @param[in] propMap map of property values
161 * @param[in,out] data FRU area data to be appended
162 */
163void appendData(const Property& key, const PropertyMap& propMap,
164 FruAreaData& data)
165{
166 auto iter = propMap.find(key);
167 if (iter != propMap.end())
168 {
169 auto value = iter->second;
Patrick Venture0b02be92018-08-31 11:55:55 -0700170 // If starts with 0x or 0X remove them
171 // ex: 0x123a just take 123a
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500172 if ((value.compare(0, 2, "0x")) == 0 ||
Patrick Venture0b02be92018-08-31 11:55:55 -0700173 (value.compare(0, 2, "0X") == 0))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500174 {
175 value.erase(0, 2);
176 }
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530177
Kirill Pakhomove1160cb2020-03-24 17:08:38 +0300178 // 6 bits for length as per FRU spec v1.0
179 // if length is greater then 63(2^6) bytes then trim the data to 63
Patrick Venture0b02be92018-08-31 11:55:55 -0700180 // bytess.
181 auto valueLength = (value.length() > maxRecordAttributeValue)
182 ? maxRecordAttributeValue
183 : value.length();
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530184 // 2 bits for type
185 // Set the type to ascii
186 uint8_t typeLength = valueLength | ipmi::fru::typeASCII;
187
188 data.emplace_back(typeLength);
Patrick Venture0b02be92018-08-31 11:55:55 -0700189 std::copy(value.begin(), value.begin() + valueLength,
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530190 std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500191 }
192 else
193 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700194 // set 0 size
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500195 data.emplace_back(typeLengthByteNull);
196 }
197}
198
Patrick Venture24c771c2019-07-25 16:34:41 -0700199std::time_t timeStringToRaw(const std::string& input)
200{
201 // TODO: For non-US region timestamps, pass in region information for the
202 // FRU to avoid the month/day swap.
203 // 2017-02-24 - 13:59:00, Tue Nov 20 23:08:00 2018
204 static const std::vector<std::string> patterns = {"%Y-%m-%d - %H:%M:%S",
205 "%a %b %d %H:%M:%S %Y"};
206
207 std::tm time = {};
208
209 for (const auto& pattern : patterns)
210 {
211 std::istringstream timeStream(input);
212 timeStream >> std::get_time(&time, pattern.c_str());
213 if (!timeStream.fail())
214 {
215 break;
216 }
217 }
218
Willy Tub2411302022-11-03 08:49:51 +0000219 return timegm(&time);
Patrick Venture24c771c2019-07-25 16:34:41 -0700220}
221
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500222/**
223 * @brief Appends Build Date
224 *
225 * @param[in] propMap map of property values
226 * @param[in/out] data FRU area to add the manfufacture date
227 */
228void appendMfgDate(const PropertyMap& propMap, FruAreaData& data)
229{
Patrick Venture0b02be92018-08-31 11:55:55 -0700230 // MFG Date/Time
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500231 auto iter = propMap.find(buildDate);
Nagaraju Gorugantib898cde2018-07-10 00:47:43 -0500232 if ((iter != propMap.end()) && (iter->second.size() > 0))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500233 {
Patrick Venture24c771c2019-07-25 16:34:41 -0700234 std::time_t raw = timeStringToRaw(iter->second);
Andres Oportus7ebd2462018-04-09 10:35:21 -0700235
236 // From FRU Spec:
237 // "Mfg. Date / Time
238 // Number of minutes from 0:00 hrs 1/1/96.
239 // LSbyte first (little endian)
240 // 00_00_00h = unspecified."
Nagaraju Gorugantib898cde2018-07-10 00:47:43 -0500241 if ((raw >= secs_from_1970_1996) && (raw <= secsToMaxMfgdate))
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500242 {
Andres Oportus7ebd2462018-04-09 10:35:21 -0700243 raw -= secs_from_1970_1996;
244 raw /= secs_per_min;
245 uint8_t fru_raw[3];
246 fru_raw[0] = raw & 0xFF;
247 fru_raw[1] = (raw >> 8) & 0xFF;
248 fru_raw[2] = (raw >> 16) & 0xFF;
249 std::copy(fru_raw, fru_raw + 3, std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500250 return;
251 }
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700252 std::fprintf(stderr, "MgfDate invalid date: %u secs since UNIX epoch\n",
253 static_cast<unsigned int>(raw));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500254 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700255 // Blank date
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500256 data.emplace_back(0);
257 data.emplace_back(0);
258 data.emplace_back(0);
259}
260
261/**
262 * @brief Builds a section of the common header
263 *
264 * @param[in] infoAreaSize size of the FRU area to write
265 * @param[in] offset Current offset for data in overall record
266 * @param[in/out] data Common Header section data container
267 */
Patrick Venture0b02be92018-08-31 11:55:55 -0700268void buildCommonHeaderSection(const uint32_t& infoAreaSize, uint16_t& offset,
269 FruAreaData& data)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500270{
Patrick Venture0b02be92018-08-31 11:55:55 -0700271 // Check if data for internal use section populated
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500272 if (infoAreaSize == 0)
273 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700274 // Indicate record not present
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500275 data.emplace_back(recordNotPresent);
276 }
277 else
278 {
Ratan Gupta2f66f002018-01-31 21:26:25 +0530279 // offset should be multiple of 8.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530280 auto remainder = offset % recordUnitOfMeasurement;
Ratan Gupta2f66f002018-01-31 21:26:25 +0530281 // add the padding bytes in the offset so that offset
282 // will be multiple of 8 byte.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530283 offset += (remainder > 0) ? recordUnitOfMeasurement - remainder : 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700284 // Place data to define offset to area data section
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530285 data.emplace_back(offset / recordUnitOfMeasurement);
Ratan Gupta2f66f002018-01-31 21:26:25 +0530286
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500287 offset += infoAreaSize;
288 }
289}
290
291/**
292 * @brief Builds the Chassis info area data section
293 *
294 * @param[in] propMap map of properties for chassis info area
295 * @return FruAreaData container with chassis info area
296 */
297FruAreaData buildChassisInfoArea(const PropertyMap& propMap)
298{
299 FruAreaData fruAreaData;
300 if (!propMap.empty())
301 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700302 // Set formatting data that goes at the beginning of the record
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500303 preFormatProcessing(false, fruAreaData);
304
Patrick Venture0b02be92018-08-31 11:55:55 -0700305 // chassis type
Oskar Senft40f59e22018-12-04 22:43:19 -0500306 appendChassisType(propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500307
Patrick Venture0b02be92018-08-31 11:55:55 -0700308 // Chasiss part number, in config.yaml it is configured as model
William A. Kennington IIIecd9f372020-05-31 15:52:09 -0700309 appendData(modelNumber, propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500310
Patrick Venture0b02be92018-08-31 11:55:55 -0700311 // Board serial number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500312 appendData(serialNumber, propMap, fruAreaData);
313
Patrick Venture0b02be92018-08-31 11:55:55 -0700314 // Indicate End of Custom Fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500315 fruAreaData.emplace_back(endOfCustomFields);
316
Patrick Venture0b02be92018-08-31 11:55:55 -0700317 // Complete record data formatting
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500318 postFormatProcessing(fruAreaData);
319 }
320 return fruAreaData;
321}
322
323/**
324 * @brief Builds the Board info area data section
325 *
326 * @param[in] propMap map of properties for board info area
327 * @return FruAreaData container with board info area
328 */
329FruAreaData buildBoardInfoArea(const PropertyMap& propMap)
330{
331 FruAreaData fruAreaData;
332 if (!propMap.empty())
333 {
334 preFormatProcessing(true, fruAreaData);
335
Patrick Venture0b02be92018-08-31 11:55:55 -0700336 // Manufacturing date
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500337 appendMfgDate(propMap, fruAreaData);
338
Patrick Venture0b02be92018-08-31 11:55:55 -0700339 // manufacturer
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500340 appendData(manufacturer, propMap, fruAreaData);
341
Patrick Venture0b02be92018-08-31 11:55:55 -0700342 // Product name/Pretty name
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500343 appendData(prettyName, propMap, fruAreaData);
344
Patrick Venture0b02be92018-08-31 11:55:55 -0700345 // Board serial number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500346 appendData(serialNumber, propMap, fruAreaData);
347
Patrick Venture0b02be92018-08-31 11:55:55 -0700348 // Board part number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500349 appendData(partNumber, propMap, fruAreaData);
350
Patrick Venture0b02be92018-08-31 11:55:55 -0700351 // FRU File ID - Empty
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500352 fruAreaData.emplace_back(typeLengthByteNull);
353
354 // Empty FRU File ID bytes
355 fruAreaData.emplace_back(recordNotPresent);
356
Patrick Venture0b02be92018-08-31 11:55:55 -0700357 // End of custom fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500358 fruAreaData.emplace_back(endOfCustomFields);
359
360 postFormatProcessing(fruAreaData);
361 }
362 return fruAreaData;
363}
364
365/**
366 * @brief Builds the Product info area data section
367 *
368 * @param[in] propMap map of FRU properties for Board info area
369 * @return FruAreaData container with product info area data
370 */
371FruAreaData buildProductInfoArea(const PropertyMap& propMap)
372{
373 FruAreaData fruAreaData;
374 if (!propMap.empty())
375 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700376 // Set formatting data that goes at the beginning of the record
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500377 preFormatProcessing(true, fruAreaData);
378
Patrick Venture0b02be92018-08-31 11:55:55 -0700379 // manufacturer
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500380 appendData(manufacturer, propMap, fruAreaData);
381
Patrick Venture0b02be92018-08-31 11:55:55 -0700382 // Product name/Pretty name
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500383 appendData(prettyName, propMap, fruAreaData);
384
Patrick Venture0b02be92018-08-31 11:55:55 -0700385 // Product part/model number
William A. Kennington IIIecd9f372020-05-31 15:52:09 -0700386 appendData(modelNumber, propMap, fruAreaData);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500387
Patrick Venture0b02be92018-08-31 11:55:55 -0700388 // Product version
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500389 appendData(version, propMap, fruAreaData);
390
Patrick Venture0b02be92018-08-31 11:55:55 -0700391 // Serial Number
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500392 appendData(serialNumber, propMap, fruAreaData);
393
Patrick Venture0b02be92018-08-31 11:55:55 -0700394 // Add Asset Tag
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500395 fruAreaData.emplace_back(recordNotPresent);
396
Patrick Venture0b02be92018-08-31 11:55:55 -0700397 // FRU File ID - Empty
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500398 fruAreaData.emplace_back(typeLengthByteNull);
399
400 // Empty FRU File ID bytes
401 fruAreaData.emplace_back(recordNotPresent);
402
Patrick Venture0b02be92018-08-31 11:55:55 -0700403 // End of custom fields
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500404 fruAreaData.emplace_back(endOfCustomFields);
405
406 postFormatProcessing(fruAreaData);
407 }
408 return fruAreaData;
409}
410
411FruAreaData buildFruAreaData(const FruInventoryData& inventory)
412{
Ratan Gupta2f66f002018-01-31 21:26:25 +0530413 FruAreaData combFruArea{};
Patrick Venture0b02be92018-08-31 11:55:55 -0700414 // Now build common header with data for this FRU Inv Record
415 // Use this variable to increment size of header as we go along to determine
416 // offset for the subsequent area offsets
Ratan Gupta2f66f002018-01-31 21:26:25 +0530417 uint16_t curDataOffset = commonHeaderFormatSize;
Patrick Venture0b02be92018-08-31 11:55:55 -0700418 // First byte is id for version of FRU Info Storage Spec used
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500419 combFruArea.emplace_back(specVersion);
420
Patrick Venture0b02be92018-08-31 11:55:55 -0700421 // 2nd byte is offset to internal use data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500422 combFruArea.emplace_back(recordNotPresent);
423
Patrick Venture0b02be92018-08-31 11:55:55 -0700424 // 3rd byte is offset to chassis data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500425 FruAreaData chassisArea;
426 auto chassisIt = inventory.find(chassis);
427 if (chassisIt != inventory.end())
428 {
Patrick Venture2e529ee2020-07-04 09:57:05 -0700429 chassisArea = buildChassisInfoArea(chassisIt->second);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500430 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530431 // update the offset to chassis data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500432 buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea);
433
Patrick Venture0b02be92018-08-31 11:55:55 -0700434 // 4th byte is offset to board data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500435 FruAreaData boardArea;
436 auto boardIt = inventory.find(board);
437 if (boardIt != inventory.end())
438 {
Patrick Venture2e529ee2020-07-04 09:57:05 -0700439 boardArea = buildBoardInfoArea(boardIt->second);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500440 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530441 // update the offset to the board data.
442 buildCommonHeaderSection(boardArea.size(), curDataOffset, combFruArea);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500443
Patrick Venture0b02be92018-08-31 11:55:55 -0700444 // 5th byte is offset to product data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500445 FruAreaData prodArea;
446 auto prodIt = inventory.find(product);
447 if (prodIt != inventory.end())
448 {
Patrick Venture2e529ee2020-07-04 09:57:05 -0700449 prodArea = buildProductInfoArea(prodIt->second);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500450 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530451 // update the offset to the product data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500452 buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea);
453
Patrick Venture0b02be92018-08-31 11:55:55 -0700454 // 6th byte is offset to multirecord data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500455 combFruArea.emplace_back(recordNotPresent);
456
Patrick Venture0b02be92018-08-31 11:55:55 -0700457 // 7th byte is PAD
Ratan Gupta2f66f002018-01-31 21:26:25 +0530458 combFruArea.emplace_back(recordNotPresent);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500459
Patrick Venture0b02be92018-08-31 11:55:55 -0700460 // 8th (Final byte of Header Format) is the checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500461 appendDataChecksum(combFruArea);
462
Patrick Venture0b02be92018-08-31 11:55:55 -0700463 // Combine everything into one full IPMI FRU specification Record
464 // add chassis use area data
465 combFruArea.insert(combFruArea.end(), chassisArea.begin(),
466 chassisArea.end());
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500467
Patrick Venture0b02be92018-08-31 11:55:55 -0700468 // add board area data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500469 combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end());
470
Patrick Venture0b02be92018-08-31 11:55:55 -0700471 // add product use area data
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500472 combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end());
473
Oskar Senft8b189412018-12-07 16:13:38 -0500474 // If area is smaller than the minimum size, pad it. This enables ipmitool
475 // to update the FRU blob with values longer than the original payload.
476 if (combFruArea.size() < fruMinSize)
477 {
478 combFruArea.resize(fruMinSize, fruPadValue);
479 }
480
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500481 return combFruArea;
482}
483
Patrick Venture0b02be92018-08-31 11:55:55 -0700484} // namespace fru
485} // namespace ipmi