xref: /openbmc/entity-manager/src/fru_device/fru_utils.cpp (revision dbf95b2c54c5a40d1ea44d650eb6aab2a4c34ba5)
14e1142d6SAlexander Hansen // SPDX-License-Identifier: Apache-2.0
24e1142d6SAlexander Hansen // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
33cbff97fSChristopher Meis 
43cbff97fSChristopher Meis #include "fru_utils.hpp"
53cbff97fSChristopher Meis 
610c57656SEd Tanous #include "gzip_utils.hpp"
710c57656SEd Tanous 
83cbff97fSChristopher Meis #include <phosphor-logging/lg2.hpp>
93cbff97fSChristopher Meis 
103cbff97fSChristopher Meis #include <array>
113cbff97fSChristopher Meis #include <cstddef>
123cbff97fSChristopher Meis #include <cstdint>
133cbff97fSChristopher Meis #include <filesystem>
143cbff97fSChristopher Meis #include <iomanip>
153cbff97fSChristopher Meis #include <numeric>
163cbff97fSChristopher Meis #include <set>
173cbff97fSChristopher Meis #include <sstream>
183cbff97fSChristopher Meis #include <string>
193cbff97fSChristopher Meis #include <vector>
203cbff97fSChristopher Meis 
213cbff97fSChristopher Meis extern "C"
223cbff97fSChristopher Meis {
233cbff97fSChristopher Meis // Include for I2C_SMBUS_BLOCK_MAX
243cbff97fSChristopher Meis #include <linux/i2c.h>
253cbff97fSChristopher Meis }
263cbff97fSChristopher Meis 
273cbff97fSChristopher Meis constexpr size_t fruVersion = 1; // Current FRU spec version number is 1
283cbff97fSChristopher Meis 
intelEpoch()293cbff97fSChristopher Meis std::tm intelEpoch()
303cbff97fSChristopher Meis {
313cbff97fSChristopher Meis     std::tm val = {};
323cbff97fSChristopher Meis     val.tm_year = 1996 - 1900;
333cbff97fSChristopher Meis     val.tm_mday = 1;
343cbff97fSChristopher Meis     return val;
353cbff97fSChristopher Meis }
363cbff97fSChristopher Meis 
sixBitToChar(uint8_t val)373cbff97fSChristopher Meis char sixBitToChar(uint8_t val)
383cbff97fSChristopher Meis {
393cbff97fSChristopher Meis     return static_cast<char>((val & 0x3f) + ' ');
403cbff97fSChristopher Meis }
413cbff97fSChristopher Meis 
bcdPlusToChar(uint8_t val)423cbff97fSChristopher Meis char bcdPlusToChar(uint8_t val)
433cbff97fSChristopher Meis {
443cbff97fSChristopher Meis     val &= 0xf;
453cbff97fSChristopher Meis     return (val < 10) ? static_cast<char>(val + '0') : bcdHighChars[val - 10];
463cbff97fSChristopher Meis }
473cbff97fSChristopher Meis 
483cbff97fSChristopher Meis enum FRUDataEncoding
493cbff97fSChristopher Meis {
503cbff97fSChristopher Meis     binary = 0x0,
513cbff97fSChristopher Meis     bcdPlus = 0x1,
523cbff97fSChristopher Meis     sixBitASCII = 0x2,
533cbff97fSChristopher Meis     languageDependent = 0x3,
543cbff97fSChristopher Meis };
553cbff97fSChristopher Meis 
563cbff97fSChristopher Meis enum MultiRecordType : uint8_t
573cbff97fSChristopher Meis {
583cbff97fSChristopher Meis     powerSupplyInfo = 0x00,
593cbff97fSChristopher Meis     dcOutput = 0x01,
603cbff97fSChristopher Meis     dcLoad = 0x02,
613cbff97fSChristopher Meis     managementAccessRecord = 0x03,
623cbff97fSChristopher Meis     baseCompatibilityRecord = 0x04,
633cbff97fSChristopher Meis     extendedCompatibilityRecord = 0x05,
643cbff97fSChristopher Meis     resvASFSMBusDeviceRecord = 0x06,
653cbff97fSChristopher Meis     resvASFLegacyDeviceAlerts = 0x07,
663cbff97fSChristopher Meis     resvASFRemoteControl = 0x08,
673cbff97fSChristopher Meis     extendedDCOutput = 0x09,
683cbff97fSChristopher Meis     extendedDCLoad = 0x0A
693cbff97fSChristopher Meis };
703cbff97fSChristopher Meis 
713cbff97fSChristopher Meis enum SubManagementAccessRecord : uint8_t
723cbff97fSChristopher Meis {
733cbff97fSChristopher Meis     systemManagementURL = 0x01,
743cbff97fSChristopher Meis     systemName = 0x02,
753cbff97fSChristopher Meis     systemPingAddress = 0x03,
763cbff97fSChristopher Meis     componentManagementURL = 0x04,
773cbff97fSChristopher Meis     componentName = 0x05,
783cbff97fSChristopher Meis     componentPingAddress = 0x06,
793cbff97fSChristopher Meis     systemUniqueID = 0x07
803cbff97fSChristopher Meis };
813cbff97fSChristopher Meis 
823cbff97fSChristopher Meis /* Decode FRU data into a std::string, given an input iterator and end. If the
833cbff97fSChristopher Meis  * state returned is fruDataOk, then the resulting string is the decoded FRU
843cbff97fSChristopher Meis  * data. The input iterator is advanced past the data consumed.
853cbff97fSChristopher Meis  *
863cbff97fSChristopher Meis  * On fruDataErr, we have lost synchronisation with the length bytes, so the
873cbff97fSChristopher Meis  * iterator is no longer usable.
883cbff97fSChristopher Meis  */
decodeFRUData(std::span<const uint8_t>::const_iterator & iter,std::span<const uint8_t>::const_iterator & end,bool isLangEng)893cbff97fSChristopher Meis std::pair<DecodeState, std::string> decodeFRUData(
903cbff97fSChristopher Meis     std::span<const uint8_t>::const_iterator& iter,
913cbff97fSChristopher Meis     std::span<const uint8_t>::const_iterator& end, bool isLangEng)
923cbff97fSChristopher Meis {
933cbff97fSChristopher Meis     std::string value;
943cbff97fSChristopher Meis     unsigned int i = 0;
953cbff97fSChristopher Meis 
963cbff97fSChristopher Meis     /* we need at least one byte to decode the type/len header */
973cbff97fSChristopher Meis     if (iter == end)
983cbff97fSChristopher Meis     {
998feb0454SAlexander Hansen         lg2::error("Truncated FRU data");
1003cbff97fSChristopher Meis         return make_pair(DecodeState::err, value);
1013cbff97fSChristopher Meis     }
1023cbff97fSChristopher Meis 
1033cbff97fSChristopher Meis     uint8_t c = *(iter++);
1043cbff97fSChristopher Meis 
1053cbff97fSChristopher Meis     /* 0xc1 is the end marker */
1063cbff97fSChristopher Meis     if (c == 0xc1)
1073cbff97fSChristopher Meis     {
1083cbff97fSChristopher Meis         return make_pair(DecodeState::end, value);
1093cbff97fSChristopher Meis     }
1103cbff97fSChristopher Meis 
1113cbff97fSChristopher Meis     /* decode type/len byte */
1123cbff97fSChristopher Meis     uint8_t type = static_cast<uint8_t>(c >> 6);
1133cbff97fSChristopher Meis     uint8_t len = static_cast<uint8_t>(c & 0x3f);
1143cbff97fSChristopher Meis 
1153cbff97fSChristopher Meis     /* we should have at least len bytes of data available overall */
1163cbff97fSChristopher Meis     if (iter + len > end)
1173cbff97fSChristopher Meis     {
1188feb0454SAlexander Hansen         lg2::error("FRU data field extends past end of FRU area data");
1193cbff97fSChristopher Meis         return make_pair(DecodeState::err, value);
1203cbff97fSChristopher Meis     }
1213cbff97fSChristopher Meis 
1223cbff97fSChristopher Meis     switch (type)
1233cbff97fSChristopher Meis     {
1243cbff97fSChristopher Meis         case FRUDataEncoding::binary:
1253cbff97fSChristopher Meis         {
1263cbff97fSChristopher Meis             std::stringstream ss;
1273cbff97fSChristopher Meis             ss << std::hex << std::setfill('0');
1283cbff97fSChristopher Meis             for (i = 0; i < len; i++, iter++)
1293cbff97fSChristopher Meis             {
1303cbff97fSChristopher Meis                 uint8_t val = static_cast<uint8_t>(*iter);
1313cbff97fSChristopher Meis                 ss << std::setw(2) << static_cast<int>(val);
1323cbff97fSChristopher Meis             }
1333cbff97fSChristopher Meis             value = ss.str();
1343cbff97fSChristopher Meis             break;
1353cbff97fSChristopher Meis         }
1363cbff97fSChristopher Meis         case FRUDataEncoding::languageDependent:
1373cbff97fSChristopher Meis             /* For language-code dependent encodings, assume 8-bit ASCII */
1383cbff97fSChristopher Meis             value = std::string(iter, iter + len);
1393cbff97fSChristopher Meis             iter += len;
1403cbff97fSChristopher Meis 
1413cbff97fSChristopher Meis             /* English text is encoded in 8-bit ASCII + Latin 1. All other
1423cbff97fSChristopher Meis              * languages are required to use 2-byte unicode. FruDevice does not
1433cbff97fSChristopher Meis              * handle unicode.
1443cbff97fSChristopher Meis              */
1453cbff97fSChristopher Meis             if (!isLangEng)
1463cbff97fSChristopher Meis             {
1478feb0454SAlexander Hansen                 lg2::error("Error: Non english string is not supported ");
1483cbff97fSChristopher Meis                 return make_pair(DecodeState::err, value);
1493cbff97fSChristopher Meis             }
1503cbff97fSChristopher Meis 
1513cbff97fSChristopher Meis             break;
1523cbff97fSChristopher Meis 
1533cbff97fSChristopher Meis         case FRUDataEncoding::bcdPlus:
1543cbff97fSChristopher Meis             value = std::string();
1553cbff97fSChristopher Meis             for (i = 0; i < len; i++, iter++)
1563cbff97fSChristopher Meis             {
1573cbff97fSChristopher Meis                 uint8_t val = *iter;
1583cbff97fSChristopher Meis                 value.push_back(bcdPlusToChar(val >> 4));
1593cbff97fSChristopher Meis                 value.push_back(bcdPlusToChar(val & 0xf));
1603cbff97fSChristopher Meis             }
1613cbff97fSChristopher Meis             break;
1623cbff97fSChristopher Meis 
1633cbff97fSChristopher Meis         case FRUDataEncoding::sixBitASCII:
1643cbff97fSChristopher Meis         {
1653cbff97fSChristopher Meis             unsigned int accum = 0;
1663cbff97fSChristopher Meis             unsigned int accumBitLen = 0;
1673cbff97fSChristopher Meis             value = std::string();
1683cbff97fSChristopher Meis             for (i = 0; i < len; i++, iter++)
1693cbff97fSChristopher Meis             {
1703cbff97fSChristopher Meis                 accum |= *iter << accumBitLen;
1713cbff97fSChristopher Meis                 accumBitLen += 8;
1723cbff97fSChristopher Meis                 while (accumBitLen >= 6)
1733cbff97fSChristopher Meis                 {
1743cbff97fSChristopher Meis                     value.push_back(sixBitToChar(accum & 0x3f));
1753cbff97fSChristopher Meis                     accum >>= 6;
1763cbff97fSChristopher Meis                     accumBitLen -= 6;
1773cbff97fSChristopher Meis                 }
1783cbff97fSChristopher Meis             }
1793cbff97fSChristopher Meis         }
1803cbff97fSChristopher Meis         break;
1813cbff97fSChristopher Meis 
1823cbff97fSChristopher Meis         default:
1833cbff97fSChristopher Meis         {
1843cbff97fSChristopher Meis             return make_pair(DecodeState::err, value);
1853cbff97fSChristopher Meis         }
1863cbff97fSChristopher Meis     }
1873cbff97fSChristopher Meis 
1883cbff97fSChristopher Meis     return make_pair(DecodeState::ok, value);
1893cbff97fSChristopher Meis }
1903cbff97fSChristopher Meis 
checkLangEng(uint8_t lang)1913cbff97fSChristopher Meis bool checkLangEng(uint8_t lang)
1923cbff97fSChristopher Meis {
1933cbff97fSChristopher Meis     // If Lang is not English then the encoding is defined as 2-byte UNICODE,
1943cbff97fSChristopher Meis     // but we don't support that.
1953cbff97fSChristopher Meis     if ((lang != 0U) && lang != 25)
1963cbff97fSChristopher Meis     {
1978feb0454SAlexander Hansen         lg2::error("Warning: languages other than English is not supported");
1983cbff97fSChristopher Meis         // Return language flag as non english
1993cbff97fSChristopher Meis         return false;
2003cbff97fSChristopher Meis     }
2013cbff97fSChristopher Meis     return true;
2023cbff97fSChristopher Meis }
2033cbff97fSChristopher Meis 
2043cbff97fSChristopher Meis /* This function verifies for other offsets to check if they are not
2053cbff97fSChristopher Meis  * falling under other field area
2063cbff97fSChristopher Meis  *
2073cbff97fSChristopher Meis  * fruBytes:    Start of Fru data
2083cbff97fSChristopher Meis  * currentArea: Index of current area offset to be compared against all area
2093cbff97fSChristopher Meis  *              offset and it is a multiple of 8 bytes as per specification
2103cbff97fSChristopher Meis  * len:         Length of current area space and it is a multiple of 8 bytes
2113cbff97fSChristopher Meis  *              as per specification
2123cbff97fSChristopher Meis  */
verifyOffset(std::span<const uint8_t> fruBytes,fruAreas currentArea,uint8_t len)2133cbff97fSChristopher Meis bool verifyOffset(std::span<const uint8_t> fruBytes, fruAreas currentArea,
2143cbff97fSChristopher Meis                   uint8_t len)
2153cbff97fSChristopher Meis {
2163cbff97fSChristopher Meis     unsigned int fruBytesSize = fruBytes.size();
2173cbff97fSChristopher Meis 
2183cbff97fSChristopher Meis     // check if Fru data has at least 8 byte header
2193cbff97fSChristopher Meis     if (fruBytesSize <= fruBlockSize)
2203cbff97fSChristopher Meis     {
2218feb0454SAlexander Hansen         lg2::error("Error: trying to parse empty FRU");
2223cbff97fSChristopher Meis         return false;
2233cbff97fSChristopher Meis     }
2243cbff97fSChristopher Meis 
2253cbff97fSChristopher Meis     // Check range of passed currentArea value
2263cbff97fSChristopher Meis     if (currentArea > fruAreas::fruAreaMultirecord)
2273cbff97fSChristopher Meis     {
2288feb0454SAlexander Hansen         lg2::error("Error: Fru area is out of range");
2293cbff97fSChristopher Meis         return false;
2303cbff97fSChristopher Meis     }
2313cbff97fSChristopher Meis 
2323cbff97fSChristopher Meis     unsigned int currentAreaIndex = getHeaderAreaFieldOffset(currentArea);
2333cbff97fSChristopher Meis     if (currentAreaIndex > fruBytesSize)
2343cbff97fSChristopher Meis     {
2358feb0454SAlexander Hansen         lg2::error("Error: Fru area index is out of range");
2363cbff97fSChristopher Meis         return false;
2373cbff97fSChristopher Meis     }
2383cbff97fSChristopher Meis 
2393cbff97fSChristopher Meis     unsigned int start = fruBytes[currentAreaIndex];
2403cbff97fSChristopher Meis     unsigned int end = start + len;
2413cbff97fSChristopher Meis 
2423cbff97fSChristopher Meis     /* Verify each offset within the range of start and end */
2433cbff97fSChristopher Meis     for (fruAreas area = fruAreas::fruAreaInternal;
2443cbff97fSChristopher Meis          area <= fruAreas::fruAreaMultirecord; ++area)
2453cbff97fSChristopher Meis     {
2463cbff97fSChristopher Meis         // skip the current offset
2473cbff97fSChristopher Meis         if (area == currentArea)
2483cbff97fSChristopher Meis         {
2493cbff97fSChristopher Meis             continue;
2503cbff97fSChristopher Meis         }
2513cbff97fSChristopher Meis 
2523cbff97fSChristopher Meis         unsigned int areaIndex = getHeaderAreaFieldOffset(area);
2533cbff97fSChristopher Meis         if (areaIndex > fruBytesSize)
2543cbff97fSChristopher Meis         {
2558feb0454SAlexander Hansen             lg2::error("Error: Fru area index is out of range");
2563cbff97fSChristopher Meis             return false;
2573cbff97fSChristopher Meis         }
2583cbff97fSChristopher Meis 
2593cbff97fSChristopher Meis         unsigned int areaOffset = fruBytes[areaIndex];
2603cbff97fSChristopher Meis         // if areaOffset is 0 means this area is not available so skip
2613cbff97fSChristopher Meis         if (areaOffset == 0)
2623cbff97fSChristopher Meis         {
2633cbff97fSChristopher Meis             continue;
2643cbff97fSChristopher Meis         }
2653cbff97fSChristopher Meis 
2663cbff97fSChristopher Meis         // check for overlapping of current offset with given areaoffset
2673cbff97fSChristopher Meis         if (areaOffset == start || (areaOffset > start && areaOffset < end))
2683cbff97fSChristopher Meis         {
2698feb0454SAlexander Hansen             lg2::error("{AREA1} offset is overlapping with {AREA2} offset",
2708feb0454SAlexander Hansen                        "AREA1", getFruAreaName(currentArea), "AREA2",
2718feb0454SAlexander Hansen                        getFruAreaName(area));
2723cbff97fSChristopher Meis             return false;
2733cbff97fSChristopher Meis         }
2743cbff97fSChristopher Meis     }
2753cbff97fSChristopher Meis     return true;
2763cbff97fSChristopher Meis }
2773cbff97fSChristopher Meis 
parseMultirecordUUID(std::span<const uint8_t> device,std::flat_map<std::string,std::string,std::less<>> & result)2783cbff97fSChristopher Meis static void parseMultirecordUUID(
2793cbff97fSChristopher Meis     std::span<const uint8_t> device,
280*dbf95b2cSEd Tanous     std::flat_map<std::string, std::string, std::less<>>& result)
2813cbff97fSChristopher Meis {
2823cbff97fSChristopher Meis     constexpr size_t uuidDataLen = 16;
2833cbff97fSChristopher Meis     constexpr size_t multiRecordHeaderLen = 5;
2843cbff97fSChristopher Meis     /* UUID record data, plus one to skip past the sub-record type byte */
2853cbff97fSChristopher Meis     constexpr size_t uuidRecordData = multiRecordHeaderLen + 1;
2863cbff97fSChristopher Meis     constexpr size_t multiRecordEndOfListMask = 0x80;
2873cbff97fSChristopher Meis     /* The UUID {00112233-4455-6677-8899-AABBCCDDEEFF} would thus be represented
2883cbff97fSChristopher Meis      * as: 0x33 0x22 0x11 0x00 0x55 0x44 0x77 0x66 0x88 0x99 0xAA 0xBB 0xCC 0xDD
2893cbff97fSChristopher Meis      * 0xEE 0xFF
2903cbff97fSChristopher Meis      */
2913cbff97fSChristopher Meis     const std::array<uint8_t, uuidDataLen> uuidCharOrder = {
2923cbff97fSChristopher Meis         3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15};
2933cbff97fSChristopher Meis     size_t offset = getHeaderAreaFieldOffset(fruAreas::fruAreaMultirecord);
2943cbff97fSChristopher Meis     if (offset >= device.size())
2953cbff97fSChristopher Meis     {
2963cbff97fSChristopher Meis         throw std::runtime_error("Multirecord UUID offset is out of range");
2973cbff97fSChristopher Meis     }
2983cbff97fSChristopher Meis     uint32_t areaOffset = device[offset];
2993cbff97fSChristopher Meis 
3003cbff97fSChristopher Meis     if (areaOffset == 0)
3013cbff97fSChristopher Meis     {
3023cbff97fSChristopher Meis         return;
3033cbff97fSChristopher Meis     }
3043cbff97fSChristopher Meis 
3053cbff97fSChristopher Meis     areaOffset *= fruBlockSize;
3063cbff97fSChristopher Meis     std::span<const uint8_t>::const_iterator fruBytesIter =
3073cbff97fSChristopher Meis         device.begin() + areaOffset;
3083cbff97fSChristopher Meis 
3093cbff97fSChristopher Meis     /* Verify area offset */
3103cbff97fSChristopher Meis     if (!verifyOffset(device, fruAreas::fruAreaMultirecord, *fruBytesIter))
3113cbff97fSChristopher Meis     {
3123cbff97fSChristopher Meis         return;
3133cbff97fSChristopher Meis     }
3143cbff97fSChristopher Meis     while (areaOffset + uuidRecordData + uuidDataLen <= device.size())
3153cbff97fSChristopher Meis     {
3163cbff97fSChristopher Meis         if ((areaOffset < device.size()) &&
3173cbff97fSChristopher Meis             (device[areaOffset] ==
3183cbff97fSChristopher Meis              (uint8_t)MultiRecordType::managementAccessRecord))
3193cbff97fSChristopher Meis         {
3203cbff97fSChristopher Meis             if ((areaOffset + multiRecordHeaderLen < device.size()) &&
3213cbff97fSChristopher Meis                 (device[areaOffset + multiRecordHeaderLen] ==
3223cbff97fSChristopher Meis                  (uint8_t)SubManagementAccessRecord::systemUniqueID))
3233cbff97fSChristopher Meis             {
3243cbff97fSChristopher Meis                 /* Layout of UUID:
3253cbff97fSChristopher Meis                  * source: https://www.ietf.org/rfc/rfc4122.txt
3263cbff97fSChristopher Meis                  *
3273cbff97fSChristopher Meis                  * UUID binary format (16 bytes):
3283cbff97fSChristopher Meis                  * 4B-2B-2B-2B-6B (big endian)
3293cbff97fSChristopher Meis                  *
3303cbff97fSChristopher Meis                  * UUID string is 36 length of characters (36 bytes):
3313cbff97fSChristopher Meis                  * 0        9    14   19   24
3323cbff97fSChristopher Meis                  * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
3333cbff97fSChristopher Meis                  *    be     be   be   be       be
3343cbff97fSChristopher Meis                  * be means it should be converted to big endian.
3353cbff97fSChristopher Meis                  */
3363cbff97fSChristopher Meis                 /* Get UUID bytes to UUID string */
3373cbff97fSChristopher Meis                 std::stringstream tmp;
3383cbff97fSChristopher Meis                 tmp << std::hex << std::setfill('0');
3393cbff97fSChristopher Meis                 for (size_t i = 0; i < uuidDataLen; i++)
3403cbff97fSChristopher Meis                 {
3413cbff97fSChristopher Meis                     tmp << std::setw(2)
3423cbff97fSChristopher Meis                         << static_cast<uint16_t>(
3433cbff97fSChristopher Meis                                device[areaOffset + uuidRecordData +
3443cbff97fSChristopher Meis                                       uuidCharOrder[i]]);
3453cbff97fSChristopher Meis                 }
3463cbff97fSChristopher Meis                 std::string uuidStr = tmp.str();
3473cbff97fSChristopher Meis                 result["MULTIRECORD_UUID"] =
3483cbff97fSChristopher Meis                     uuidStr.substr(0, 8) + '-' + uuidStr.substr(8, 4) + '-' +
3493cbff97fSChristopher Meis                     uuidStr.substr(12, 4) + '-' + uuidStr.substr(16, 4) + '-' +
3503cbff97fSChristopher Meis                     uuidStr.substr(20, 12);
3513cbff97fSChristopher Meis                 break;
3523cbff97fSChristopher Meis             }
3533cbff97fSChristopher Meis         }
3543cbff97fSChristopher Meis         if ((device[areaOffset + 1] & multiRecordEndOfListMask) != 0)
3553cbff97fSChristopher Meis         {
3563cbff97fSChristopher Meis             break;
3573cbff97fSChristopher Meis         }
3583cbff97fSChristopher Meis         areaOffset = areaOffset + device[areaOffset + 2] + multiRecordHeaderLen;
3593cbff97fSChristopher Meis     }
3603cbff97fSChristopher Meis }
3613cbff97fSChristopher Meis 
decodeField(std::span<const uint8_t>::const_iterator & fruBytesIter,std::span<const uint8_t>::const_iterator & fruBytesIterEndArea,const std::vector<std::string> & fruAreaFieldNames,size_t & fieldIndex,DecodeState & state,bool isLangEng,const fruAreas & area,std::flat_map<std::string,std::string,std::less<>> & result)3623cbff97fSChristopher Meis resCodes decodeField(
3633cbff97fSChristopher Meis     std::span<const uint8_t>::const_iterator& fruBytesIter,
3643cbff97fSChristopher Meis     std::span<const uint8_t>::const_iterator& fruBytesIterEndArea,
3653cbff97fSChristopher Meis     const std::vector<std::string>& fruAreaFieldNames, size_t& fieldIndex,
3663cbff97fSChristopher Meis     DecodeState& state, bool isLangEng, const fruAreas& area,
367*dbf95b2cSEd Tanous     std::flat_map<std::string, std::string, std::less<>>& result)
3683cbff97fSChristopher Meis {
3693cbff97fSChristopher Meis     auto res = decodeFRUData(fruBytesIter, fruBytesIterEndArea, isLangEng);
3703cbff97fSChristopher Meis     state = res.first;
3713cbff97fSChristopher Meis     std::string value = res.second;
3723cbff97fSChristopher Meis     std::string name;
3733cbff97fSChristopher Meis     bool isCustomField = false;
3743cbff97fSChristopher Meis     if (fieldIndex < fruAreaFieldNames.size())
3753cbff97fSChristopher Meis     {
3763cbff97fSChristopher Meis         name = std::string(getFruAreaName(area)) + "_" +
3773cbff97fSChristopher Meis                fruAreaFieldNames.at(fieldIndex);
3783cbff97fSChristopher Meis     }
3793cbff97fSChristopher Meis     else
3803cbff97fSChristopher Meis     {
3813cbff97fSChristopher Meis         isCustomField = true;
3823cbff97fSChristopher Meis         name = std::string(getFruAreaName(area)) + "_" + fruCustomFieldName +
3833cbff97fSChristopher Meis                std::to_string(fieldIndex - fruAreaFieldNames.size() + 1);
3843cbff97fSChristopher Meis     }
3853cbff97fSChristopher Meis 
3863cbff97fSChristopher Meis     if (state == DecodeState::ok)
3873cbff97fSChristopher Meis     {
3883cbff97fSChristopher Meis         // Strip non null characters and trailing spaces from the end
3893cbff97fSChristopher Meis         value.erase(
3903cbff97fSChristopher Meis             std::find_if(value.rbegin(), value.rend(),
3913cbff97fSChristopher Meis                          [](char ch) { return ((ch != 0) && (ch != ' ')); })
3923cbff97fSChristopher Meis                 .base(),
3933cbff97fSChristopher Meis             value.end());
3943cbff97fSChristopher Meis         if (isCustomField)
3953cbff97fSChristopher Meis         {
3963cbff97fSChristopher Meis             // Some MAC addresses are stored in a custom field, with
3973cbff97fSChristopher Meis             // "MAC:" prefixed on the value.  If we see that, create a
3983cbff97fSChristopher Meis             // new field with the decoded data
3993cbff97fSChristopher Meis             if (value.starts_with("MAC: "))
4003cbff97fSChristopher Meis             {
4013cbff97fSChristopher Meis                 result["MAC_" + name] = value.substr(5);
4023cbff97fSChristopher Meis             }
4033cbff97fSChristopher Meis         }
4043cbff97fSChristopher Meis         result[name] = std::move(value);
4053cbff97fSChristopher Meis         ++fieldIndex;
4063cbff97fSChristopher Meis     }
4073cbff97fSChristopher Meis     else if (state == DecodeState::err)
4083cbff97fSChristopher Meis     {
4098feb0454SAlexander Hansen         lg2::error("Error while parsing {NAME}", "NAME", name);
4103cbff97fSChristopher Meis 
4113cbff97fSChristopher Meis         // Cancel decoding if failed to parse any of mandatory
4123cbff97fSChristopher Meis         // fields
4133cbff97fSChristopher Meis         if (fieldIndex < fruAreaFieldNames.size())
4143cbff97fSChristopher Meis         {
4158feb0454SAlexander Hansen             lg2::error("Failed to parse mandatory field ");
4163cbff97fSChristopher Meis             return resCodes::resErr;
4173cbff97fSChristopher Meis         }
4183cbff97fSChristopher Meis         return resCodes::resWarn;
4193cbff97fSChristopher Meis     }
4203cbff97fSChristopher Meis     else
4213cbff97fSChristopher Meis     {
4223cbff97fSChristopher Meis         if (fieldIndex < fruAreaFieldNames.size())
4233cbff97fSChristopher Meis         {
4248feb0454SAlexander Hansen             lg2::error(
4258feb0454SAlexander Hansen                 "Mandatory fields absent in FRU area {AREA} after {NAME}",
4268feb0454SAlexander Hansen                 "AREA", getFruAreaName(area), "NAME", name);
4273cbff97fSChristopher Meis             return resCodes::resWarn;
4283cbff97fSChristopher Meis         }
4293cbff97fSChristopher Meis     }
4303cbff97fSChristopher Meis     return resCodes::resOK;
4313cbff97fSChristopher Meis }
4323cbff97fSChristopher Meis 
formatIPMIFRU(std::span<const uint8_t> fruBytes,std::flat_map<std::string,std::string,std::less<>> & result)4333cbff97fSChristopher Meis resCodes formatIPMIFRU(
4343cbff97fSChristopher Meis     std::span<const uint8_t> fruBytes,
435*dbf95b2cSEd Tanous     std::flat_map<std::string, std::string, std::less<>>& result)
4363cbff97fSChristopher Meis {
4373cbff97fSChristopher Meis     resCodes ret = resCodes::resOK;
4383cbff97fSChristopher Meis     if (fruBytes.size() <= fruBlockSize)
4393cbff97fSChristopher Meis     {
4408feb0454SAlexander Hansen         lg2::error("Error: trying to parse empty FRU ");
4413cbff97fSChristopher Meis         return resCodes::resErr;
4423cbff97fSChristopher Meis     }
4433cbff97fSChristopher Meis     result["Common_Format_Version"] =
4443cbff97fSChristopher Meis         std::to_string(static_cast<int>(*fruBytes.begin()));
4453cbff97fSChristopher Meis 
4463cbff97fSChristopher Meis     const std::vector<std::string>* fruAreaFieldNames = nullptr;
4473cbff97fSChristopher Meis 
4483cbff97fSChristopher Meis     // Don't parse Internal and Multirecord areas
4493cbff97fSChristopher Meis     for (fruAreas area = fruAreas::fruAreaChassis;
4503cbff97fSChristopher Meis          area <= fruAreas::fruAreaProduct; ++area)
4513cbff97fSChristopher Meis     {
4523cbff97fSChristopher Meis         size_t offset = *(fruBytes.begin() + getHeaderAreaFieldOffset(area));
4533cbff97fSChristopher Meis         if (offset == 0)
4543cbff97fSChristopher Meis         {
4553cbff97fSChristopher Meis             continue;
4563cbff97fSChristopher Meis         }
4573cbff97fSChristopher Meis         offset *= fruBlockSize;
4583cbff97fSChristopher Meis         std::span<const uint8_t>::const_iterator fruBytesIter =
4593cbff97fSChristopher Meis             fruBytes.begin() + offset;
4603cbff97fSChristopher Meis         if (fruBytesIter + fruBlockSize >= fruBytes.end())
4613cbff97fSChristopher Meis         {
4628feb0454SAlexander Hansen             lg2::error("Not enough data to parse ");
4633cbff97fSChristopher Meis             return resCodes::resErr;
4643cbff97fSChristopher Meis         }
4653cbff97fSChristopher Meis         // check for format version 1
4663cbff97fSChristopher Meis         if (*fruBytesIter != 0x01)
4673cbff97fSChristopher Meis         {
4688feb0454SAlexander Hansen             lg2::error("Unexpected version {VERSION}", "VERSION",
4698feb0454SAlexander Hansen                        *fruBytesIter);
4703cbff97fSChristopher Meis             return resCodes::resErr;
4713cbff97fSChristopher Meis         }
4723cbff97fSChristopher Meis         ++fruBytesIter;
4733cbff97fSChristopher Meis 
4743cbff97fSChristopher Meis         /* Verify other area offset for overlap with current area by passing
4753cbff97fSChristopher Meis          * length of current area offset pointed by *fruBytesIter
4763cbff97fSChristopher Meis          */
4773cbff97fSChristopher Meis         if (!verifyOffset(fruBytes, area, *fruBytesIter))
4783cbff97fSChristopher Meis         {
4793cbff97fSChristopher Meis             return resCodes::resErr;
4803cbff97fSChristopher Meis         }
4813cbff97fSChristopher Meis 
4823cbff97fSChristopher Meis         size_t fruAreaSize = *fruBytesIter * fruBlockSize;
4833cbff97fSChristopher Meis         std::span<const uint8_t>::const_iterator fruBytesIterEndArea =
4843cbff97fSChristopher Meis             fruBytes.begin() + offset + fruAreaSize - 1;
4853cbff97fSChristopher Meis         ++fruBytesIter;
4863cbff97fSChristopher Meis 
4873cbff97fSChristopher Meis         uint8_t fruComputedChecksum =
4883cbff97fSChristopher Meis             calculateChecksum(fruBytes.begin() + offset, fruBytesIterEndArea);
4893cbff97fSChristopher Meis         if (fruComputedChecksum != *fruBytesIterEndArea)
4903cbff97fSChristopher Meis         {
4913cbff97fSChristopher Meis             std::stringstream ss;
4923cbff97fSChristopher Meis             ss << std::hex << std::setfill('0');
4933cbff97fSChristopher Meis             ss << "Checksum error in FRU area " << getFruAreaName(area) << "\n";
4943cbff97fSChristopher Meis             ss << "\tComputed checksum: 0x" << std::setw(2)
4953cbff97fSChristopher Meis                << static_cast<int>(fruComputedChecksum) << "\n";
4963cbff97fSChristopher Meis             ss << "\tThe read checksum: 0x" << std::setw(2)
4973cbff97fSChristopher Meis                << static_cast<int>(*fruBytesIterEndArea) << "\n";
4988feb0454SAlexander Hansen             lg2::error("{ERR}", "ERR", ss.str());
4993cbff97fSChristopher Meis             ret = resCodes::resWarn;
5003cbff97fSChristopher Meis         }
5013cbff97fSChristopher Meis 
5023cbff97fSChristopher Meis         /* Set default language flag to true as Chassis Fru area are always
5033cbff97fSChristopher Meis          * encoded in English defined in Section 10 of Fru specification
5043cbff97fSChristopher Meis          */
5053cbff97fSChristopher Meis 
5063cbff97fSChristopher Meis         bool isLangEng = true;
5073cbff97fSChristopher Meis         switch (area)
5083cbff97fSChristopher Meis         {
5093cbff97fSChristopher Meis             case fruAreas::fruAreaChassis:
5103cbff97fSChristopher Meis             {
5113cbff97fSChristopher Meis                 result["CHASSIS_TYPE"] =
5123cbff97fSChristopher Meis                     std::to_string(static_cast<int>(*fruBytesIter));
5133cbff97fSChristopher Meis                 fruBytesIter += 1;
5143cbff97fSChristopher Meis                 fruAreaFieldNames = &chassisFruAreas;
5153cbff97fSChristopher Meis                 break;
5163cbff97fSChristopher Meis             }
5173cbff97fSChristopher Meis             case fruAreas::fruAreaBoard:
5183cbff97fSChristopher Meis             {
5193cbff97fSChristopher Meis                 uint8_t lang = *fruBytesIter;
5203cbff97fSChristopher Meis                 result["BOARD_LANGUAGE_CODE"] =
5213cbff97fSChristopher Meis                     std::to_string(static_cast<int>(lang));
5223cbff97fSChristopher Meis                 isLangEng = checkLangEng(lang);
5233cbff97fSChristopher Meis                 fruBytesIter += 1;
5243cbff97fSChristopher Meis 
5253cbff97fSChristopher Meis                 unsigned int minutes =
5263cbff97fSChristopher Meis                     *fruBytesIter | *(fruBytesIter + 1) << 8 |
5273cbff97fSChristopher Meis                     *(fruBytesIter + 2) << 16;
5283cbff97fSChristopher Meis                 std::tm fruTime = intelEpoch();
5293cbff97fSChristopher Meis                 std::time_t timeValue = timegm(&fruTime);
5303cbff97fSChristopher Meis                 timeValue += static_cast<long>(minutes) * 60;
5313cbff97fSChristopher Meis                 fruTime = *std::gmtime(&timeValue);
5323cbff97fSChristopher Meis 
5333cbff97fSChristopher Meis                 // Tue Nov 20 23:08:00 2018
5343cbff97fSChristopher Meis                 std::array<char, 32> timeString = {};
5353cbff97fSChristopher Meis                 auto bytes = std::strftime(timeString.data(), timeString.size(),
5363cbff97fSChristopher Meis                                            "%Y%m%dT%H%M%SZ", &fruTime);
5373cbff97fSChristopher Meis                 if (bytes == 0)
5383cbff97fSChristopher Meis                 {
5398feb0454SAlexander Hansen                     lg2::error("invalid time string encountered");
5403cbff97fSChristopher Meis                     return resCodes::resErr;
5413cbff97fSChristopher Meis                 }
5423cbff97fSChristopher Meis 
5433cbff97fSChristopher Meis                 result["BOARD_MANUFACTURE_DATE"] =
5443cbff97fSChristopher Meis                     std::string_view(timeString.data(), bytes);
5453cbff97fSChristopher Meis                 fruBytesIter += 3;
5463cbff97fSChristopher Meis                 fruAreaFieldNames = &boardFruAreas;
5473cbff97fSChristopher Meis                 break;
5483cbff97fSChristopher Meis             }
5493cbff97fSChristopher Meis             case fruAreas::fruAreaProduct:
5503cbff97fSChristopher Meis             {
5513cbff97fSChristopher Meis                 uint8_t lang = *fruBytesIter;
5523cbff97fSChristopher Meis                 result["PRODUCT_LANGUAGE_CODE"] =
5533cbff97fSChristopher Meis                     std::to_string(static_cast<int>(lang));
5543cbff97fSChristopher Meis                 isLangEng = checkLangEng(lang);
5553cbff97fSChristopher Meis                 fruBytesIter += 1;
5563cbff97fSChristopher Meis                 fruAreaFieldNames = &productFruAreas;
5573cbff97fSChristopher Meis                 break;
5583cbff97fSChristopher Meis             }
5593cbff97fSChristopher Meis             default:
5603cbff97fSChristopher Meis             {
5618feb0454SAlexander Hansen                 lg2::error(
5628feb0454SAlexander Hansen                     "Internal error: unexpected FRU area index: {INDEX} ",
5638feb0454SAlexander Hansen                     "INDEX", static_cast<int>(area));
5643cbff97fSChristopher Meis                 return resCodes::resErr;
5653cbff97fSChristopher Meis             }
5663cbff97fSChristopher Meis         }
5673cbff97fSChristopher Meis         size_t fieldIndex = 0;
5683cbff97fSChristopher Meis         DecodeState state = DecodeState::ok;
5693cbff97fSChristopher Meis         do
5703cbff97fSChristopher Meis         {
5713cbff97fSChristopher Meis             resCodes decodeRet = decodeField(fruBytesIter, fruBytesIterEndArea,
5723cbff97fSChristopher Meis                                              *fruAreaFieldNames, fieldIndex,
5733cbff97fSChristopher Meis                                              state, isLangEng, area, result);
5743cbff97fSChristopher Meis             if (decodeRet == resCodes::resErr)
5753cbff97fSChristopher Meis             {
5763cbff97fSChristopher Meis                 return resCodes::resErr;
5773cbff97fSChristopher Meis             }
5783cbff97fSChristopher Meis             if (decodeRet == resCodes::resWarn)
5793cbff97fSChristopher Meis             {
5803cbff97fSChristopher Meis                 ret = decodeRet;
5813cbff97fSChristopher Meis             }
5823cbff97fSChristopher Meis         } while (state == DecodeState::ok);
5833cbff97fSChristopher Meis         for (; fruBytesIter < fruBytesIterEndArea; fruBytesIter++)
5843cbff97fSChristopher Meis         {
5853cbff97fSChristopher Meis             uint8_t c = *fruBytesIter;
5863cbff97fSChristopher Meis             if (c != 0U)
5873cbff97fSChristopher Meis             {
5888feb0454SAlexander Hansen                 lg2::error("Non-zero byte after EndOfFields in FRU area {AREA}",
5898feb0454SAlexander Hansen                            "AREA", getFruAreaName(area));
5903cbff97fSChristopher Meis                 ret = resCodes::resWarn;
5913cbff97fSChristopher Meis                 break;
5923cbff97fSChristopher Meis             }
5933cbff97fSChristopher Meis         }
5943cbff97fSChristopher Meis     }
5953cbff97fSChristopher Meis 
5963cbff97fSChristopher Meis     /* Parsing the Multirecord UUID */
5973cbff97fSChristopher Meis     parseMultirecordUUID(fruBytes, result);
5983cbff97fSChristopher Meis 
5993cbff97fSChristopher Meis     return ret;
6003cbff97fSChristopher Meis }
6013cbff97fSChristopher Meis 
6023cbff97fSChristopher Meis // Calculate new checksum for fru info area
calculateChecksum(std::span<const uint8_t>::const_iterator iter,std::span<const uint8_t>::const_iterator end)6033cbff97fSChristopher Meis uint8_t calculateChecksum(std::span<const uint8_t>::const_iterator iter,
6043cbff97fSChristopher Meis                           std::span<const uint8_t>::const_iterator end)
6053cbff97fSChristopher Meis {
6063cbff97fSChristopher Meis     constexpr int checksumMod = 256;
6073cbff97fSChristopher Meis     uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
6083cbff97fSChristopher Meis     return (checksumMod - sum) % checksumMod;
6093cbff97fSChristopher Meis }
6103cbff97fSChristopher Meis 
calculateChecksum(std::span<const uint8_t> fruAreaData)6113cbff97fSChristopher Meis uint8_t calculateChecksum(std::span<const uint8_t> fruAreaData)
6123cbff97fSChristopher Meis {
6133cbff97fSChristopher Meis     return calculateChecksum(fruAreaData.begin(), fruAreaData.end());
6143cbff97fSChristopher Meis }
6153cbff97fSChristopher Meis 
6163cbff97fSChristopher Meis // Update new fru area length &
6173cbff97fSChristopher Meis // Update checksum at new checksum location
6183cbff97fSChristopher Meis // Return the offset of the area checksum byte
updateFRUAreaLenAndChecksum(std::vector<uint8_t> & fruData,size_t fruAreaStart,size_t fruAreaEndOfFieldsOffset,size_t fruAreaEndOffset)6193cbff97fSChristopher Meis unsigned int updateFRUAreaLenAndChecksum(
6203cbff97fSChristopher Meis     std::vector<uint8_t>& fruData, size_t fruAreaStart,
6213cbff97fSChristopher Meis     size_t fruAreaEndOfFieldsOffset, size_t fruAreaEndOffset)
6223cbff97fSChristopher Meis {
6233cbff97fSChristopher Meis     size_t traverseFRUAreaIndex = fruAreaEndOfFieldsOffset - fruAreaStart;
6243cbff97fSChristopher Meis 
6253cbff97fSChristopher Meis     // fill zeros for any remaining unused space
6263cbff97fSChristopher Meis     std::fill(fruData.begin() + fruAreaEndOfFieldsOffset,
6273cbff97fSChristopher Meis               fruData.begin() + fruAreaEndOffset, 0);
6283cbff97fSChristopher Meis 
6293cbff97fSChristopher Meis     size_t mod = traverseFRUAreaIndex % fruBlockSize;
6303cbff97fSChristopher Meis     size_t checksumLoc = 0;
6313cbff97fSChristopher Meis     if (mod == 0U)
6323cbff97fSChristopher Meis     {
6333cbff97fSChristopher Meis         traverseFRUAreaIndex += (fruBlockSize);
6343cbff97fSChristopher Meis         checksumLoc = fruAreaEndOfFieldsOffset + (fruBlockSize - 1);
6353cbff97fSChristopher Meis     }
6363cbff97fSChristopher Meis     else
6373cbff97fSChristopher Meis     {
6383cbff97fSChristopher Meis         traverseFRUAreaIndex += (fruBlockSize - mod);
6393cbff97fSChristopher Meis         checksumLoc = fruAreaEndOfFieldsOffset + (fruBlockSize - mod - 1);
6403cbff97fSChristopher Meis     }
6413cbff97fSChristopher Meis 
6423cbff97fSChristopher Meis     size_t newFRUAreaLen =
6433cbff97fSChristopher Meis         (traverseFRUAreaIndex / fruBlockSize) +
6443cbff97fSChristopher Meis         static_cast<unsigned long>((traverseFRUAreaIndex % fruBlockSize) != 0);
6453cbff97fSChristopher Meis     size_t fruAreaLengthLoc = fruAreaStart + 1;
6463cbff97fSChristopher Meis     fruData[fruAreaLengthLoc] = static_cast<uint8_t>(newFRUAreaLen);
6473cbff97fSChristopher Meis 
6483cbff97fSChristopher Meis     // Calculate new checksum
6493cbff97fSChristopher Meis     std::vector<uint8_t> finalFRUData;
6503cbff97fSChristopher Meis     std::copy_n(fruData.begin() + fruAreaStart, checksumLoc - fruAreaStart,
6513cbff97fSChristopher Meis                 std::back_inserter(finalFRUData));
6523cbff97fSChristopher Meis 
6533cbff97fSChristopher Meis     fruData[checksumLoc] = calculateChecksum(finalFRUData);
6543cbff97fSChristopher Meis     return checksumLoc;
6553cbff97fSChristopher Meis }
6563cbff97fSChristopher Meis 
getFieldLength(uint8_t fruFieldTypeLenValue)6573cbff97fSChristopher Meis ssize_t getFieldLength(uint8_t fruFieldTypeLenValue)
6583cbff97fSChristopher Meis {
6593cbff97fSChristopher Meis     constexpr uint8_t typeLenMask = 0x3F;
6603cbff97fSChristopher Meis     constexpr uint8_t endOfFields = 0xC1;
6613cbff97fSChristopher Meis     if (fruFieldTypeLenValue == endOfFields)
6623cbff97fSChristopher Meis     {
6633cbff97fSChristopher Meis         return -1;
6643cbff97fSChristopher Meis     }
6653cbff97fSChristopher Meis     return fruFieldTypeLenValue & typeLenMask;
6663cbff97fSChristopher Meis }
6673cbff97fSChristopher Meis 
validateHeader(const std::array<uint8_t,I2C_SMBUS_BLOCK_MAX> & blockData)6683cbff97fSChristopher Meis bool validateHeader(const std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData)
6693cbff97fSChristopher Meis {
6703cbff97fSChristopher Meis     // ipmi spec format version number is currently at 1, verify it
6713cbff97fSChristopher Meis     if (blockData[0] != fruVersion)
6723cbff97fSChristopher Meis     {
6733cbff97fSChristopher Meis         lg2::debug(
6743cbff97fSChristopher Meis             "FRU spec version {VERSION} not supported. Supported version is {SUPPORTED_VERSION}",
6753cbff97fSChristopher Meis             "VERSION", lg2::hex, blockData[0], "SUPPORTED_VERSION", lg2::hex,
6763cbff97fSChristopher Meis             fruVersion);
6773cbff97fSChristopher Meis         return false;
6783cbff97fSChristopher Meis     }
6793cbff97fSChristopher Meis 
6803cbff97fSChristopher Meis     // verify pad is set to 0
6813cbff97fSChristopher Meis     if (blockData[6] != 0x0)
6823cbff97fSChristopher Meis     {
6833cbff97fSChristopher Meis         lg2::debug("Pad value in header is non zero, value is {VALUE}", "VALUE",
6843cbff97fSChristopher Meis                    lg2::hex, blockData[6]);
6853cbff97fSChristopher Meis         return false;
6863cbff97fSChristopher Meis     }
6873cbff97fSChristopher Meis 
6883cbff97fSChristopher Meis     // verify offsets are 0, or don't point to another offset
6893cbff97fSChristopher Meis     std::set<uint8_t> foundOffsets;
6903cbff97fSChristopher Meis     for (int ii = 1; ii < 6; ii++)
6913cbff97fSChristopher Meis     {
6923cbff97fSChristopher Meis         if (blockData[ii] == 0)
6933cbff97fSChristopher Meis         {
6943cbff97fSChristopher Meis             continue;
6953cbff97fSChristopher Meis         }
6963cbff97fSChristopher Meis         auto inserted = foundOffsets.insert(blockData[ii]);
6973cbff97fSChristopher Meis         if (!inserted.second)
6983cbff97fSChristopher Meis         {
6993cbff97fSChristopher Meis             return false;
7003cbff97fSChristopher Meis         }
7013cbff97fSChristopher Meis     }
7023cbff97fSChristopher Meis 
7033cbff97fSChristopher Meis     // validate checksum
7043cbff97fSChristopher Meis     size_t sum = 0;
7053cbff97fSChristopher Meis     for (int jj = 0; jj < 7; jj++)
7063cbff97fSChristopher Meis     {
7073cbff97fSChristopher Meis         sum += blockData[jj];
7083cbff97fSChristopher Meis     }
7093cbff97fSChristopher Meis     sum = (256 - sum) & 0xFF;
7103cbff97fSChristopher Meis 
7113cbff97fSChristopher Meis     if (sum != blockData[7])
7123cbff97fSChristopher Meis     {
7133cbff97fSChristopher Meis         lg2::debug(
7143cbff97fSChristopher Meis             "Checksum {CHECKSUM} is invalid. calculated checksum is {CALCULATED_CHECKSUM}",
7153cbff97fSChristopher Meis             "CHECKSUM", lg2::hex, blockData[7], "CALCULATED_CHECKSUM", lg2::hex,
7163cbff97fSChristopher Meis             sum);
7173cbff97fSChristopher Meis         return false;
7183cbff97fSChristopher Meis     }
7193cbff97fSChristopher Meis     return true;
7203cbff97fSChristopher Meis }
7213cbff97fSChristopher Meis 
parseMacFromGzipXmlHeader(FRUReader & reader,off_t offset)72210c57656SEd Tanous std::string parseMacFromGzipXmlHeader(FRUReader& reader, off_t offset)
7233cbff97fSChristopher Meis {
72410c57656SEd Tanous     // gzip starts at offset 512. Read that from the FRU
72510c57656SEd Tanous     // in this case, 32k bytes is enough to hold the whole manifest
72610c57656SEd Tanous     constexpr size_t totalReadSize = 32UL * 1024UL;
72710c57656SEd Tanous 
72810c57656SEd Tanous     std::vector<uint8_t> headerData(totalReadSize, 0U);
72910c57656SEd Tanous 
73010c57656SEd Tanous     int rc = reader.read(offset, totalReadSize, headerData.data());
73110c57656SEd Tanous     if (rc <= 0)
73210c57656SEd Tanous     {
73310c57656SEd Tanous         return {};
73410c57656SEd Tanous     }
73510c57656SEd Tanous 
73610c57656SEd Tanous     std::optional<std::string> xml = gzipInflate(headerData);
73710c57656SEd Tanous     if (!xml)
73810c57656SEd Tanous     {
73910c57656SEd Tanous         return {};
74010c57656SEd Tanous     }
74110c57656SEd Tanous     std::vector<std::string> node = getNodeFromXml(
74210c57656SEd Tanous         *xml, "/GSSKU/BoardInfo/Main/NIC/*[Mode = 'Dedicated']/MacAddr0");
74310c57656SEd Tanous     if (node.empty())
74410c57656SEd Tanous     {
74510c57656SEd Tanous         lg2::debug("No mac address found in gzip xml header");
74610c57656SEd Tanous         return {};
74710c57656SEd Tanous     }
74810c57656SEd Tanous     if (node.size() > 1)
74910c57656SEd Tanous     {
75010c57656SEd Tanous         lg2::warning("Multiple mac addresses found in gzip xml header");
75110c57656SEd Tanous     }
75210c57656SEd Tanous     return node[0];
75310c57656SEd Tanous }
75410c57656SEd Tanous 
findFRUHeader(FRUReader & reader,const std::string & errorHelp,off_t startingOffset)75510c57656SEd Tanous std::optional<FruSections> findFRUHeader(
75610c57656SEd Tanous     FRUReader& reader, const std::string& errorHelp, off_t startingOffset)
75710c57656SEd Tanous {
75810c57656SEd Tanous     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData = {};
75910c57656SEd Tanous     if (reader.read(startingOffset, 0x8, blockData.data()) < 0)
7603cbff97fSChristopher Meis     {
7618feb0454SAlexander Hansen         lg2::error("failed to read {ERR} base offset {OFFSET}", "ERR",
76210c57656SEd Tanous                    errorHelp, "OFFSET", startingOffset);
76310c57656SEd Tanous         return std::nullopt;
7643cbff97fSChristopher Meis     }
7653cbff97fSChristopher Meis 
7663cbff97fSChristopher Meis     // check the header checksum
7673cbff97fSChristopher Meis     if (validateHeader(blockData))
7683cbff97fSChristopher Meis     {
76910c57656SEd Tanous         FruSections fru = {};
77010c57656SEd Tanous         static_assert(fru.ipmiFruBlock.size() == blockData.size(),
77110c57656SEd Tanous                       "size mismatch in block data");
77210c57656SEd Tanous         std::memcpy(fru.ipmiFruBlock.data(), blockData.data(),
77310c57656SEd Tanous                     I2C_SMBUS_BLOCK_MAX);
77410c57656SEd Tanous         fru.IpmiFruOffset = startingOffset;
77510c57656SEd Tanous         return fru;
7763cbff97fSChristopher Meis     }
7773cbff97fSChristopher Meis 
7783cbff97fSChristopher Meis     // only continue the search if we just looked at 0x0.
77910c57656SEd Tanous     if (startingOffset != 0)
7803cbff97fSChristopher Meis     {
78110c57656SEd Tanous         return std::nullopt;
7823cbff97fSChristopher Meis     }
7833cbff97fSChristopher Meis 
7843cbff97fSChristopher Meis     // now check for special cases where the IPMI data is at an offset
7853cbff97fSChristopher Meis 
7863cbff97fSChristopher Meis     // check if blockData starts with tyanHeader
7873cbff97fSChristopher Meis     const std::vector<uint8_t> tyanHeader = {'$', 'T', 'Y', 'A', 'N', '$'};
7883cbff97fSChristopher Meis     if (blockData.size() >= tyanHeader.size() &&
7893cbff97fSChristopher Meis         std::equal(tyanHeader.begin(), tyanHeader.end(), blockData.begin()))
7903cbff97fSChristopher Meis     {
7913cbff97fSChristopher Meis         // look for the FRU header at offset 0x6000
79210c57656SEd Tanous         off_t tyanOffset = 0x6000;
79310c57656SEd Tanous         return findFRUHeader(reader, errorHelp, tyanOffset);
7943cbff97fSChristopher Meis     }
7953cbff97fSChristopher Meis 
796cc2d9e27SMark Kuo     // check if blockData starts with gigabyteHeader
797cc2d9e27SMark Kuo     const std::vector<uint8_t> gigabyteHeader = {'G', 'I', 'G', 'A',
798cc2d9e27SMark Kuo                                                  'B', 'Y', 'T', 'E'};
799cc2d9e27SMark Kuo     if (blockData.size() >= gigabyteHeader.size() &&
800cc2d9e27SMark Kuo         std::equal(gigabyteHeader.begin(), gigabyteHeader.end(),
801cc2d9e27SMark Kuo                    blockData.begin()))
802cc2d9e27SMark Kuo     {
803cc2d9e27SMark Kuo         // look for the FRU header at offset 0x4000
80410c57656SEd Tanous         off_t gbOffset = 0x4000;
80510c57656SEd Tanous         auto sections = findFRUHeader(reader, errorHelp, gbOffset);
80610c57656SEd Tanous         if (sections)
80710c57656SEd Tanous         {
80810c57656SEd Tanous             lg2::debug("succeeded on GB parse");
80910c57656SEd Tanous             // GB xml header is at 512 bytes
81010c57656SEd Tanous             sections->GigabyteXmlOffset = 512;
81110c57656SEd Tanous         }
81210c57656SEd Tanous         else
81310c57656SEd Tanous         {
81410c57656SEd Tanous             lg2::error("Failed on GB parse");
81510c57656SEd Tanous         }
81610c57656SEd Tanous         return sections;
817cc2d9e27SMark Kuo     }
818cc2d9e27SMark Kuo 
8193cbff97fSChristopher Meis     lg2::debug("Illegal header {HEADER} base offset {OFFSET}", "HEADER",
82010c57656SEd Tanous                errorHelp, "OFFSET", startingOffset);
8213cbff97fSChristopher Meis 
82210c57656SEd Tanous     return std::nullopt;
8233cbff97fSChristopher Meis }
8243cbff97fSChristopher Meis 
readFRUContents(FRUReader & reader,const std::string & errorHelp)8253cbff97fSChristopher Meis std::pair<std::vector<uint8_t>, bool> readFRUContents(
8263cbff97fSChristopher Meis     FRUReader& reader, const std::string& errorHelp)
8273cbff97fSChristopher Meis {
8283cbff97fSChristopher Meis     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
82910c57656SEd Tanous     std::optional<FruSections> sections = findFRUHeader(reader, errorHelp, 0);
83010c57656SEd Tanous     if (!sections)
8313cbff97fSChristopher Meis     {
8323cbff97fSChristopher Meis         return {{}, false};
8333cbff97fSChristopher Meis     }
83410c57656SEd Tanous     const off_t baseOffset = sections->IpmiFruOffset;
83510c57656SEd Tanous     std::memcpy(blockData.data(), sections->ipmiFruBlock.data(),
83610c57656SEd Tanous                 blockData.size());
8373cbff97fSChristopher Meis     std::vector<uint8_t> device;
8385df916f1SAlexander Hansen     device.insert(device.end(), blockData.begin(),
8395df916f1SAlexander Hansen                   std::next(blockData.begin(), 8));
8403cbff97fSChristopher Meis 
8413cbff97fSChristopher Meis     bool hasMultiRecords = false;
8423cbff97fSChristopher Meis     size_t fruLength = fruBlockSize; // At least FRU header is present
8433cbff97fSChristopher Meis     unsigned int prevOffset = 0;
8443cbff97fSChristopher Meis     for (fruAreas area = fruAreas::fruAreaInternal;
8453cbff97fSChristopher Meis          area <= fruAreas::fruAreaMultirecord; ++area)
8463cbff97fSChristopher Meis     {
8473cbff97fSChristopher Meis         // Offset value can be 255.
8483cbff97fSChristopher Meis         unsigned int areaOffset = device[getHeaderAreaFieldOffset(area)];
8493cbff97fSChristopher Meis         if (areaOffset == 0)
8503cbff97fSChristopher Meis         {
8513cbff97fSChristopher Meis             continue;
8523cbff97fSChristopher Meis         }
8533cbff97fSChristopher Meis 
8543cbff97fSChristopher Meis         /* Check for offset order, as per Section 17 of FRU specification, FRU
8553cbff97fSChristopher Meis          * information areas are required to be in order in FRU data layout
8563cbff97fSChristopher Meis          * which means all offset value should be in increasing order or can be
8573cbff97fSChristopher Meis          * 0 if that area is not present
8583cbff97fSChristopher Meis          */
8593cbff97fSChristopher Meis         if (areaOffset <= prevOffset)
8603cbff97fSChristopher Meis         {
8618feb0454SAlexander Hansen             lg2::error(
8628feb0454SAlexander Hansen                 "Fru area offsets are not in required order as per Section 17 of Fru specification");
8633cbff97fSChristopher Meis             return {{}, true};
8643cbff97fSChristopher Meis         }
8653cbff97fSChristopher Meis         prevOffset = areaOffset;
8663cbff97fSChristopher Meis 
8673cbff97fSChristopher Meis         // MultiRecords are different. area is not tracking section, it's
8683cbff97fSChristopher Meis         // walking the common header.
8693cbff97fSChristopher Meis         if (area == fruAreas::fruAreaMultirecord)
8703cbff97fSChristopher Meis         {
8713cbff97fSChristopher Meis             hasMultiRecords = true;
8723cbff97fSChristopher Meis             break;
8733cbff97fSChristopher Meis         }
8743cbff97fSChristopher Meis 
8753cbff97fSChristopher Meis         areaOffset *= fruBlockSize;
8763cbff97fSChristopher Meis 
8773cbff97fSChristopher Meis         if (reader.read(baseOffset + areaOffset, 0x2, blockData.data()) < 0)
8783cbff97fSChristopher Meis         {
8798feb0454SAlexander Hansen             lg2::error("failed to read {ERR} base offset {OFFSET}", "ERR",
8808feb0454SAlexander Hansen                        errorHelp, "OFFSET", baseOffset);
8813cbff97fSChristopher Meis             return {{}, true};
8823cbff97fSChristopher Meis         }
8833cbff97fSChristopher Meis 
8843cbff97fSChristopher Meis         // Ignore data type (blockData is already unsigned).
8853cbff97fSChristopher Meis         size_t length = blockData[1] * fruBlockSize;
8863cbff97fSChristopher Meis         areaOffset += length;
8873cbff97fSChristopher Meis         fruLength = (areaOffset > fruLength) ? areaOffset : fruLength;
8883cbff97fSChristopher Meis     }
8893cbff97fSChristopher Meis 
8903cbff97fSChristopher Meis     if (hasMultiRecords)
8913cbff97fSChristopher Meis     {
8923cbff97fSChristopher Meis         // device[area count] is the index to the last area because the 0th
8933cbff97fSChristopher Meis         // entry is not an offset in the common header.
8943cbff97fSChristopher Meis         unsigned int areaOffset =
8953cbff97fSChristopher Meis             device[getHeaderAreaFieldOffset(fruAreas::fruAreaMultirecord)];
8963cbff97fSChristopher Meis         areaOffset *= fruBlockSize;
8973cbff97fSChristopher Meis 
8983cbff97fSChristopher Meis         // the multi-area record header is 5 bytes long.
8993cbff97fSChristopher Meis         constexpr size_t multiRecordHeaderSize = 5;
9003cbff97fSChristopher Meis         constexpr uint8_t multiRecordEndOfListMask = 0x80;
9013cbff97fSChristopher Meis 
9023cbff97fSChristopher Meis         // Sanity hard-limit to 64KB.
9033cbff97fSChristopher Meis         while (areaOffset < std::numeric_limits<uint16_t>::max())
9043cbff97fSChristopher Meis         {
9053cbff97fSChristopher Meis             // In multi-area, the area offset points to the 0th record, each
9063cbff97fSChristopher Meis             // record has 3 bytes of the header we care about.
9073cbff97fSChristopher Meis             if (reader.read(baseOffset + areaOffset, 0x3, blockData.data()) < 0)
9083cbff97fSChristopher Meis             {
9098feb0454SAlexander Hansen                 lg2::error("failed to read {STR} base offset {OFFSET}", "STR",
9108feb0454SAlexander Hansen                            errorHelp, "OFFSET", baseOffset);
9113cbff97fSChristopher Meis                 return {{}, true};
9123cbff97fSChristopher Meis             }
9133cbff97fSChristopher Meis 
9143cbff97fSChristopher Meis             // Ok, let's check the record length, which is in bytes (unsigned,
9153cbff97fSChristopher Meis             // up to 255, so blockData should hold uint8_t not char)
9163cbff97fSChristopher Meis             size_t recordLength = blockData[2];
9173cbff97fSChristopher Meis             areaOffset += (recordLength + multiRecordHeaderSize);
9183cbff97fSChristopher Meis             fruLength = (areaOffset > fruLength) ? areaOffset : fruLength;
9193cbff97fSChristopher Meis 
9203cbff97fSChristopher Meis             // If this is the end of the list bail.
9213cbff97fSChristopher Meis             if ((blockData[1] & multiRecordEndOfListMask) != 0)
9223cbff97fSChristopher Meis             {
9233cbff97fSChristopher Meis                 break;
9243cbff97fSChristopher Meis             }
9253cbff97fSChristopher Meis         }
9263cbff97fSChristopher Meis     }
9273cbff97fSChristopher Meis 
9283cbff97fSChristopher Meis     // You already copied these first 8 bytes (the ipmi fru header size)
9293cbff97fSChristopher Meis     fruLength -= std::min(fruBlockSize, fruLength);
9303cbff97fSChristopher Meis 
9313cbff97fSChristopher Meis     int readOffset = fruBlockSize;
9323cbff97fSChristopher Meis 
9333cbff97fSChristopher Meis     while (fruLength > 0)
9343cbff97fSChristopher Meis     {
9353cbff97fSChristopher Meis         size_t requestLength =
9363cbff97fSChristopher Meis             std::min(static_cast<size_t>(I2C_SMBUS_BLOCK_MAX), fruLength);
9373cbff97fSChristopher Meis 
9383cbff97fSChristopher Meis         if (reader.read(baseOffset + readOffset, requestLength,
9393cbff97fSChristopher Meis                         blockData.data()) < 0)
9403cbff97fSChristopher Meis         {
9418feb0454SAlexander Hansen             lg2::error("failed to read {ERR} base offset {OFFSET}", "ERR",
9428feb0454SAlexander Hansen                        errorHelp, "OFFSET", baseOffset);
9433cbff97fSChristopher Meis             return {{}, true};
9443cbff97fSChristopher Meis         }
9453cbff97fSChristopher Meis 
9463cbff97fSChristopher Meis         device.insert(device.end(), blockData.begin(),
9475df916f1SAlexander Hansen                       std::next(blockData.begin(), requestLength));
9483cbff97fSChristopher Meis 
9493cbff97fSChristopher Meis         readOffset += requestLength;
9503cbff97fSChristopher Meis         fruLength -= std::min(requestLength, fruLength);
9513cbff97fSChristopher Meis     }
9523cbff97fSChristopher Meis 
95310c57656SEd Tanous     if (sections->GigabyteXmlOffset != 0)
95410c57656SEd Tanous     {
95510c57656SEd Tanous         std::string macAddress =
95610c57656SEd Tanous             parseMacFromGzipXmlHeader(reader, sections->GigabyteXmlOffset);
95710c57656SEd Tanous         if (!macAddress.empty())
95810c57656SEd Tanous         {
95910c57656SEd Tanous             // launder the mac address as we expect into
96010c57656SEd Tanous             // BOARD_INFO_AM2 to allow the rest of the
96110c57656SEd Tanous             // system to use it
96210c57656SEd Tanous             std::string mac = std::format("MAC: {}", macAddress);
96310c57656SEd Tanous             updateAddProperty(mac, "BOARD_INFO_AM2", device);
96410c57656SEd Tanous         }
96510c57656SEd Tanous     }
96610c57656SEd Tanous 
9673cbff97fSChristopher Meis     return {device, true};
9683cbff97fSChristopher Meis }
9693cbff97fSChristopher Meis 
getHeaderAreaFieldOffset(fruAreas area)9703cbff97fSChristopher Meis unsigned int getHeaderAreaFieldOffset(fruAreas area)
9713cbff97fSChristopher Meis {
9723cbff97fSChristopher Meis     return static_cast<unsigned int>(area) + 1;
9733cbff97fSChristopher Meis }
9743cbff97fSChristopher Meis 
getFRUInfo(const uint16_t & bus,const uint8_t & address)9753cbff97fSChristopher Meis std::vector<uint8_t>& getFRUInfo(const uint16_t& bus, const uint8_t& address)
9763cbff97fSChristopher Meis {
9773cbff97fSChristopher Meis     auto deviceMap = busMap.find(bus);
9783cbff97fSChristopher Meis     if (deviceMap == busMap.end())
9793cbff97fSChristopher Meis     {
9803cbff97fSChristopher Meis         throw std::invalid_argument("Invalid Bus.");
9813cbff97fSChristopher Meis     }
9823cbff97fSChristopher Meis     auto device = deviceMap->second->find(address);
9833cbff97fSChristopher Meis     if (device == deviceMap->second->end())
9843cbff97fSChristopher Meis     {
9853cbff97fSChristopher Meis         throw std::invalid_argument("Invalid Address.");
9863cbff97fSChristopher Meis     }
9873cbff97fSChristopher Meis     std::vector<uint8_t>& ret = device->second;
9883cbff97fSChristopher Meis 
9893cbff97fSChristopher Meis     return ret;
9903cbff97fSChristopher Meis }
9913cbff97fSChristopher Meis 
updateHeaderChecksum(std::vector<uint8_t> & fruData)99225680e3dSMarc Olberding static bool updateHeaderChecksum(std::vector<uint8_t>& fruData)
993cf28896cSNaresh Solanki {
99425680e3dSMarc Olberding     if (fruData.size() < fruBlockSize)
995cf28896cSNaresh Solanki     {
996cf28896cSNaresh Solanki         lg2::debug("FRU data is too small to contain a valid header.");
997cf28896cSNaresh Solanki         return false;
998cf28896cSNaresh Solanki     }
999cf28896cSNaresh Solanki 
100025680e3dSMarc Olberding     uint8_t& checksumInBytes = fruData[7];
100125680e3dSMarc Olberding     uint8_t checksum =
100225680e3dSMarc Olberding         calculateChecksum({fruData.begin(), fruData.begin() + 7});
100325680e3dSMarc Olberding     std::swap(checksumInBytes, checksum);
100425680e3dSMarc Olberding 
100525680e3dSMarc Olberding     if (checksumInBytes != checksum)
1006cf28896cSNaresh Solanki     {
1007cf28896cSNaresh Solanki         lg2::debug(
1008cf28896cSNaresh Solanki             "FRU header checksum updated from {OLD_CHECKSUM} to {NEW_CHECKSUM}",
100925680e3dSMarc Olberding             "OLD_CHECKSUM", static_cast<int>(checksum), "NEW_CHECKSUM",
101025680e3dSMarc Olberding             static_cast<int>(checksumInBytes));
1011cf28896cSNaresh Solanki     }
1012cf28896cSNaresh Solanki     return true;
1013cf28896cSNaresh Solanki }
1014cf28896cSNaresh Solanki 
updateAreaChecksum(std::vector<uint8_t> & fruArea)101525680e3dSMarc Olberding bool updateAreaChecksum(std::vector<uint8_t>& fruArea)
1016cf28896cSNaresh Solanki {
1017cf28896cSNaresh Solanki     if (fruArea.size() < fruBlockSize)
1018cf28896cSNaresh Solanki     {
1019cf28896cSNaresh Solanki         lg2::debug("FRU area is too small to contain a valid header.");
1020cf28896cSNaresh Solanki         return false;
1021cf28896cSNaresh Solanki     }
1022cf28896cSNaresh Solanki     if (fruArea.size() % fruBlockSize != 0)
1023cf28896cSNaresh Solanki     {
1024cf28896cSNaresh Solanki         lg2::debug("FRU area size is not a multiple of {SIZE} bytes.", "SIZE",
1025cf28896cSNaresh Solanki                    fruBlockSize);
1026cf28896cSNaresh Solanki         return false;
1027cf28896cSNaresh Solanki     }
1028cf28896cSNaresh Solanki 
1029cf28896cSNaresh Solanki     uint8_t oldcksum = fruArea[fruArea.size() - 1];
1030cf28896cSNaresh Solanki 
1031cf28896cSNaresh Solanki     fruArea[fruArea.size() - 1] =
1032cf28896cSNaresh Solanki         0; // Reset checksum byte to 0 before recalculating
1033cf28896cSNaresh Solanki     fruArea[fruArea.size() - 1] = calculateChecksum(fruArea);
1034cf28896cSNaresh Solanki 
1035cf28896cSNaresh Solanki     if (oldcksum != fruArea[fruArea.size() - 1])
1036cf28896cSNaresh Solanki     {
1037cf28896cSNaresh Solanki         lg2::debug(
1038cf28896cSNaresh Solanki             "FRU area checksum updated from {OLD_CHECKSUM} to {NEW_CHECKSUM}",
1039cf28896cSNaresh Solanki             "OLD_CHECKSUM", static_cast<int>(oldcksum), "NEW_CHECKSUM",
1040cf28896cSNaresh Solanki             static_cast<int>(fruArea[fruArea.size() - 1]));
1041cf28896cSNaresh Solanki     }
1042cf28896cSNaresh Solanki     return true;
1043cf28896cSNaresh Solanki }
1044cf28896cSNaresh Solanki 
calculateAreaSize(fruAreas area,std::span<const uint8_t> fruData,size_t areaOffset)104525680e3dSMarc Olberding static std::optional<size_t> calculateAreaSize(
104625680e3dSMarc Olberding     fruAreas area, std::span<const uint8_t> fruData, size_t areaOffset)
104725680e3dSMarc Olberding {
104825680e3dSMarc Olberding     switch (area)
104925680e3dSMarc Olberding     {
105025680e3dSMarc Olberding         case fruAreas::fruAreaChassis:
105125680e3dSMarc Olberding         case fruAreas::fruAreaBoard:
105225680e3dSMarc Olberding         case fruAreas::fruAreaProduct:
105325680e3dSMarc Olberding             if (areaOffset + 1 >= fruData.size())
105425680e3dSMarc Olberding             {
105525680e3dSMarc Olberding                 return std::nullopt;
105625680e3dSMarc Olberding             }
105725680e3dSMarc Olberding             return fruData[areaOffset + 1] * fruBlockSize; // Area size in bytes
105825680e3dSMarc Olberding         case fruAreas::fruAreaInternal:
105925680e3dSMarc Olberding         {
106025680e3dSMarc Olberding             // Internal area size: It is difference between the next area
106125680e3dSMarc Olberding             // offset and current area offset
106225680e3dSMarc Olberding             for (fruAreas areaIt = fruAreas::fruAreaChassis;
106325680e3dSMarc Olberding                  areaIt <= fruAreas::fruAreaMultirecord; ++areaIt)
106425680e3dSMarc Olberding             {
106525680e3dSMarc Olberding                 size_t headerOffset = getHeaderAreaFieldOffset(areaIt);
106625680e3dSMarc Olberding                 if (headerOffset >= fruData.size())
106725680e3dSMarc Olberding                 {
106825680e3dSMarc Olberding                     return std::nullopt;
106925680e3dSMarc Olberding                 }
107025680e3dSMarc Olberding                 size_t nextAreaOffset = fruData[headerOffset];
107125680e3dSMarc Olberding                 if (nextAreaOffset != 0)
107225680e3dSMarc Olberding                 {
107325680e3dSMarc Olberding                     return nextAreaOffset * fruBlockSize - areaOffset;
107425680e3dSMarc Olberding                 }
107525680e3dSMarc Olberding             }
107625680e3dSMarc Olberding             return std::nullopt;
107725680e3dSMarc Olberding         }
107825680e3dSMarc Olberding         break;
107925680e3dSMarc Olberding         case fruAreas::fruAreaMultirecord:
108025680e3dSMarc Olberding             // Multirecord area size.
108125680e3dSMarc Olberding             return fruData.size() - areaOffset; // Area size in bytes
108225680e3dSMarc Olberding         default:
108325680e3dSMarc Olberding             lg2::error("Invalid FRU area: {AREA}", "AREA",
108425680e3dSMarc Olberding                        static_cast<int>(area));
108525680e3dSMarc Olberding     }
108625680e3dSMarc Olberding     return std::nullopt;
108725680e3dSMarc Olberding }
108825680e3dSMarc Olberding 
getBlockCount(size_t byteCount)108925680e3dSMarc Olberding static size_t getBlockCount(size_t byteCount)
109025680e3dSMarc Olberding {
109125680e3dSMarc Olberding     size_t blocks = (byteCount + fruBlockSize - 1) / fruBlockSize;
109225680e3dSMarc Olberding     // if we're perfectly aligned, we need another block for the checksum
109325680e3dSMarc Olberding     if ((byteCount % fruBlockSize) == 0)
109425680e3dSMarc Olberding     {
109525680e3dSMarc Olberding         blocks++;
109625680e3dSMarc Olberding     }
109725680e3dSMarc Olberding     return blocks;
109825680e3dSMarc Olberding }
109925680e3dSMarc Olberding 
disassembleFruData(std::vector<uint8_t> & fruData,std::vector<std::vector<uint8_t>> & areasData)1100cf28896cSNaresh Solanki bool disassembleFruData(std::vector<uint8_t>& fruData,
1101cf28896cSNaresh Solanki                         std::vector<std::vector<uint8_t>>& areasData)
1102cf28896cSNaresh Solanki {
1103cf28896cSNaresh Solanki     if (fruData.size() < 8)
1104cf28896cSNaresh Solanki     {
1105cf28896cSNaresh Solanki         lg2::debug("FRU data is too small to contain a valid header.");
1106cf28896cSNaresh Solanki         return false;
1107cf28896cSNaresh Solanki     }
1108cf28896cSNaresh Solanki 
1109cf28896cSNaresh Solanki     // Clear areasData before disassembling
1110cf28896cSNaresh Solanki     areasData.clear();
1111cf28896cSNaresh Solanki 
1112cf28896cSNaresh Solanki     // Iterate through all areas & store each area data in a vector.
1113cf28896cSNaresh Solanki     for (fruAreas area = fruAreas::fruAreaInternal;
1114cf28896cSNaresh Solanki          area <= fruAreas::fruAreaMultirecord; ++area)
1115cf28896cSNaresh Solanki     {
1116cf28896cSNaresh Solanki         size_t areaOffset = fruData[getHeaderAreaFieldOffset(area)];
1117cf28896cSNaresh Solanki 
1118cf28896cSNaresh Solanki         if (areaOffset == 0)
1119cf28896cSNaresh Solanki         {
1120cf28896cSNaresh Solanki             // Store empty area data for areas that are not present
1121cf28896cSNaresh Solanki             areasData.emplace_back();
1122cf28896cSNaresh Solanki             continue;               // Skip areas that are not present
1123cf28896cSNaresh Solanki         }
1124cf28896cSNaresh Solanki         areaOffset *= fruBlockSize; // Convert to byte offset
112525680e3dSMarc Olberding 
112625680e3dSMarc Olberding         std::optional<size_t> areaSize =
112725680e3dSMarc Olberding             calculateAreaSize(area, fruData, areaOffset);
112825680e3dSMarc Olberding         if (!areaSize)
1129cf28896cSNaresh Solanki         {
1130cf28896cSNaresh Solanki             return false;
1131cf28896cSNaresh Solanki         }
1132cf28896cSNaresh Solanki 
113325680e3dSMarc Olberding         if ((areaOffset + *areaSize) > fruData.size())
1134cf28896cSNaresh Solanki         {
1135cf28896cSNaresh Solanki             lg2::error("Area offset + size exceeds FRU data size.");
1136cf28896cSNaresh Solanki             return false;
1137cf28896cSNaresh Solanki         }
113825680e3dSMarc Olberding 
113925680e3dSMarc Olberding         areasData.emplace_back(fruData.begin() + areaOffset,
114025680e3dSMarc Olberding                                fruData.begin() + areaOffset + *areaSize);
1141cf28896cSNaresh Solanki     }
114225680e3dSMarc Olberding 
1143cf28896cSNaresh Solanki     return true;
1144cf28896cSNaresh Solanki }
1145cf28896cSNaresh Solanki 
114625680e3dSMarc Olberding struct FieldInfo
1147cf28896cSNaresh Solanki {
114825680e3dSMarc Olberding     size_t length;
114925680e3dSMarc Olberding     size_t index;
115025680e3dSMarc Olberding };
115125680e3dSMarc Olberding 
findOrCreateField(std::vector<uint8_t> & areaData,const std::string & propertyName,const fruAreas & fruAreaToUpdate)115225680e3dSMarc Olberding static std::optional<FieldInfo> findOrCreateField(
115325680e3dSMarc Olberding     std::vector<uint8_t>& areaData, const std::string& propertyName,
115425680e3dSMarc Olberding     const fruAreas& fruAreaToUpdate)
115525680e3dSMarc Olberding {
115625680e3dSMarc Olberding     int fieldIndex = 0;
1157cf28896cSNaresh Solanki     int fieldLength = 0;
1158cf28896cSNaresh Solanki     std::string areaName = propertyName.substr(0, propertyName.find('_'));
1159cf28896cSNaresh Solanki     std::string propertyNamePrefix = areaName + "_";
116025680e3dSMarc Olberding     const std::vector<std::string>* fruAreaFieldNames = nullptr;
1161cf28896cSNaresh Solanki 
1162cf28896cSNaresh Solanki     switch (fruAreaToUpdate)
1163cf28896cSNaresh Solanki     {
1164cf28896cSNaresh Solanki         case fruAreas::fruAreaChassis:
1165cf28896cSNaresh Solanki             fruAreaFieldNames = &chassisFruAreas;
1166cf28896cSNaresh Solanki             fieldIndex = 3;
1167cf28896cSNaresh Solanki             break;
1168cf28896cSNaresh Solanki         case fruAreas::fruAreaBoard:
1169cf28896cSNaresh Solanki             fruAreaFieldNames = &boardFruAreas;
1170cf28896cSNaresh Solanki             fieldIndex = 6;
1171cf28896cSNaresh Solanki             break;
1172cf28896cSNaresh Solanki         case fruAreas::fruAreaProduct:
1173cf28896cSNaresh Solanki             fruAreaFieldNames = &productFruAreas;
1174cf28896cSNaresh Solanki             fieldIndex = 3;
1175cf28896cSNaresh Solanki             break;
1176cf28896cSNaresh Solanki         default:
117725680e3dSMarc Olberding             lg2::info("Invalid FRU area: {AREA}", "AREA",
1178cf28896cSNaresh Solanki                       static_cast<int>(fruAreaToUpdate));
117925680e3dSMarc Olberding             return std::nullopt;
1180cf28896cSNaresh Solanki     }
118125680e3dSMarc Olberding 
1182cf28896cSNaresh Solanki     for (const auto& field : *fruAreaFieldNames)
1183cf28896cSNaresh Solanki     {
1184cf28896cSNaresh Solanki         fieldLength = getFieldLength(areaData[fieldIndex]);
1185cf28896cSNaresh Solanki         if (fieldLength < 0)
1186cf28896cSNaresh Solanki         {
1187cf28896cSNaresh Solanki             areaData.insert(areaData.begin() + fieldIndex, 0xc0);
1188cf28896cSNaresh Solanki             fieldLength = 0;
1189cf28896cSNaresh Solanki         }
1190cf28896cSNaresh Solanki 
1191cf28896cSNaresh Solanki         if (propertyNamePrefix + field == propertyName)
1192cf28896cSNaresh Solanki         {
119325680e3dSMarc Olberding             return FieldInfo{static_cast<size_t>(fieldLength),
119425680e3dSMarc Olberding                              static_cast<size_t>(fieldIndex)};
1195cf28896cSNaresh Solanki         }
1196cf28896cSNaresh Solanki         fieldIndex += 1 + fieldLength;
1197cf28896cSNaresh Solanki     }
119825680e3dSMarc Olberding 
1199cf28896cSNaresh Solanki     size_t pos = propertyName.find(fruCustomFieldName);
120025680e3dSMarc Olberding     if (pos == std::string::npos)
1201cf28896cSNaresh Solanki     {
120225680e3dSMarc Olberding         return std::nullopt;
120325680e3dSMarc Olberding     }
120425680e3dSMarc Olberding 
1205cf28896cSNaresh Solanki     // Get field after pos
1206cf28896cSNaresh Solanki     std::string customFieldIdx =
1207cf28896cSNaresh Solanki         propertyName.substr(pos + fruCustomFieldName.size());
1208cf28896cSNaresh Solanki 
1209cf28896cSNaresh Solanki     // Check if customFieldIdx is a number
121025680e3dSMarc Olberding     if (!std::all_of(customFieldIdx.begin(), customFieldIdx.end(), ::isdigit))
1211cf28896cSNaresh Solanki     {
121225680e3dSMarc Olberding         return std::nullopt;
121325680e3dSMarc Olberding     }
121425680e3dSMarc Olberding 
1215cf28896cSNaresh Solanki     size_t customFieldIndex = std::stoi(customFieldIdx);
121625680e3dSMarc Olberding 
121725680e3dSMarc Olberding     // insert custom fields up to the index we want
1218cf28896cSNaresh Solanki     for (size_t i = 0; i < customFieldIndex; i++)
1219cf28896cSNaresh Solanki     {
1220cf28896cSNaresh Solanki         fieldLength = getFieldLength(areaData[fieldIndex]);
1221cf28896cSNaresh Solanki         if (fieldLength < 0)
1222cf28896cSNaresh Solanki         {
1223cf28896cSNaresh Solanki             areaData.insert(areaData.begin() + fieldIndex, 0xc0);
1224cf28896cSNaresh Solanki             fieldLength = 0;
1225cf28896cSNaresh Solanki         }
1226cf28896cSNaresh Solanki         fieldIndex += 1 + fieldLength;
1227cf28896cSNaresh Solanki     }
122825680e3dSMarc Olberding 
1229cf28896cSNaresh Solanki     fieldIndex -= (fieldLength + 1);
1230cf28896cSNaresh Solanki     fieldLength = getFieldLength(areaData[fieldIndex]);
123125680e3dSMarc Olberding     return FieldInfo{static_cast<size_t>(fieldLength),
123225680e3dSMarc Olberding                      static_cast<size_t>(fieldIndex)};
1233cf28896cSNaresh Solanki }
1234cf28896cSNaresh Solanki 
findEndOfFieldMarker(std::span<uint8_t> bytes)123525680e3dSMarc Olberding static std::optional<size_t> findEndOfFieldMarker(std::span<uint8_t> bytes)
1236cf28896cSNaresh Solanki {
123725680e3dSMarc Olberding     // we're skipping the checksum
123825680e3dSMarc Olberding     // this function assumes a properly sized and formatted area
123925680e3dSMarc Olberding     static uint8_t constexpr endOfFieldsByte = 0xc1;
124025680e3dSMarc Olberding     for (int index = bytes.size() - 2; index >= 0; --index)
124125680e3dSMarc Olberding     {
124225680e3dSMarc Olberding         if (bytes[index] == endOfFieldsByte)
124325680e3dSMarc Olberding         {
124425680e3dSMarc Olberding             return index;
124525680e3dSMarc Olberding         }
124625680e3dSMarc Olberding     }
124725680e3dSMarc Olberding     return std::nullopt;
124825680e3dSMarc Olberding }
124925680e3dSMarc Olberding 
getNonPaddedSizeOfArea(std::span<uint8_t> bytes)125025680e3dSMarc Olberding static std::optional<size_t> getNonPaddedSizeOfArea(std::span<uint8_t> bytes)
125125680e3dSMarc Olberding {
125225680e3dSMarc Olberding     if (auto endOfFields = findEndOfFieldMarker(bytes))
125325680e3dSMarc Olberding     {
125425680e3dSMarc Olberding         return *endOfFields + 1;
125525680e3dSMarc Olberding     }
125625680e3dSMarc Olberding     return std::nullopt;
125725680e3dSMarc Olberding }
125825680e3dSMarc Olberding 
setField(const fruAreas & fruAreaToUpdate,std::vector<uint8_t> & areaData,const std::string & propertyName,const std::string & value)125925680e3dSMarc Olberding bool setField(const fruAreas& fruAreaToUpdate, std::vector<uint8_t>& areaData,
126025680e3dSMarc Olberding               const std::string& propertyName, const std::string& value)
126125680e3dSMarc Olberding {
126225680e3dSMarc Olberding     if (value.size() == 1 || value.size() > 63)
126325680e3dSMarc Olberding     {
126425680e3dSMarc Olberding         lg2::error("Invalid value {VALUE} for field {PROP}", "VALUE", value,
126525680e3dSMarc Olberding                    "PROP", propertyName);
126625680e3dSMarc Olberding         return false;
126725680e3dSMarc Olberding     }
126825680e3dSMarc Olberding 
126925680e3dSMarc Olberding     // This is inneficient, but the alternative requires
127025680e3dSMarc Olberding     // a bunch of complicated indexing and search to
127125680e3dSMarc Olberding     // figure out if we cross a block boundary
127225680e3dSMarc Olberding     // if we feel that this is too inneficient in the future,
127325680e3dSMarc Olberding     // we can implement that.
127425680e3dSMarc Olberding     std::vector<uint8_t> tmpBuffer = areaData;
127525680e3dSMarc Olberding 
127625680e3dSMarc Olberding     auto fieldInfo =
127725680e3dSMarc Olberding         findOrCreateField(tmpBuffer, propertyName, fruAreaToUpdate);
127825680e3dSMarc Olberding 
127925680e3dSMarc Olberding     if (!fieldInfo)
128025680e3dSMarc Olberding     {
128125680e3dSMarc Olberding         lg2::error("Field {FIELD} not found in area {AREA}", "FIELD",
1282cf28896cSNaresh Solanki                    propertyName, "AREA", getFruAreaName(fruAreaToUpdate));
1283cf28896cSNaresh Solanki         return false;
1284cf28896cSNaresh Solanki     }
1285cf28896cSNaresh Solanki 
128625680e3dSMarc Olberding     auto fieldIt = tmpBuffer.begin() + fieldInfo->index;
1287cf28896cSNaresh Solanki     // Erase the existing field content.
128825680e3dSMarc Olberding     tmpBuffer.erase(fieldIt, fieldIt + fieldInfo->length + 1);
1289cf28896cSNaresh Solanki     // Insert the new field value
129025680e3dSMarc Olberding     tmpBuffer.insert(fieldIt, 0xc0 | value.size());
129125680e3dSMarc Olberding     tmpBuffer.insert_range(fieldIt + 1, value);
1292cf28896cSNaresh Solanki 
129325680e3dSMarc Olberding     auto newSize = getNonPaddedSizeOfArea(tmpBuffer);
129425680e3dSMarc Olberding     auto oldSize = getNonPaddedSizeOfArea(areaData);
1295cf28896cSNaresh Solanki 
129625680e3dSMarc Olberding     if (!oldSize || !newSize)
1297cf28896cSNaresh Solanki     {
129825680e3dSMarc Olberding         lg2::error("Failed to find the size of the area");
1299cf28896cSNaresh Solanki         return false;
1300cf28896cSNaresh Solanki     }
1301cf28896cSNaresh Solanki 
130225680e3dSMarc Olberding     size_t newSizePadded = getBlockCount(*newSize);
1303cf28896cSNaresh Solanki #ifndef ENABLE_FRU_AREA_RESIZE
130425680e3dSMarc Olberding 
130525680e3dSMarc Olberding     size_t oldSizePadded = getBlockCount(*oldSize);
130625680e3dSMarc Olberding 
130725680e3dSMarc Olberding     if (newSizePadded != oldSizePadded)
1308cf28896cSNaresh Solanki     {
130925680e3dSMarc Olberding         lg2::error(
1310cf28896cSNaresh Solanki             "FRU area {AREA} resize is disabled, cannot increase size from {OLD_SIZE} to {NEW_SIZE}",
1311cf28896cSNaresh Solanki             "AREA", getFruAreaName(fruAreaToUpdate), "OLD_SIZE",
131225680e3dSMarc Olberding             static_cast<int>(oldSizePadded), "NEW_SIZE",
131325680e3dSMarc Olberding             static_cast<int>(newSizePadded));
1314cf28896cSNaresh Solanki         return false;
1315cf28896cSNaresh Solanki     }
131625680e3dSMarc Olberding #endif
131725680e3dSMarc Olberding     // Resize the buffer as per numOfBlocks & pad with zeros
131825680e3dSMarc Olberding     tmpBuffer.resize(newSizePadded * fruBlockSize, 0);
1319cf28896cSNaresh Solanki 
1320cf28896cSNaresh Solanki     // Update the length field
132125680e3dSMarc Olberding     tmpBuffer[1] = newSizePadded;
132225680e3dSMarc Olberding     updateAreaChecksum(tmpBuffer);
132325680e3dSMarc Olberding 
132425680e3dSMarc Olberding     areaData = std::move(tmpBuffer);
1325cf28896cSNaresh Solanki 
1326cf28896cSNaresh Solanki     return true;
1327cf28896cSNaresh Solanki }
1328cf28896cSNaresh Solanki 
assembleFruData(std::vector<uint8_t> & fruData,const std::vector<std::vector<uint8_t>> & areasData)1329cf28896cSNaresh Solanki bool assembleFruData(std::vector<uint8_t>& fruData,
1330cf28896cSNaresh Solanki                      const std::vector<std::vector<uint8_t>>& areasData)
1331cf28896cSNaresh Solanki {
133225680e3dSMarc Olberding     for (const auto& area : areasData)
133325680e3dSMarc Olberding     {
133425680e3dSMarc Olberding         if ((area.size() % fruBlockSize) != 0U)
133525680e3dSMarc Olberding         {
133625680e3dSMarc Olberding             lg2::error("unaligned area sent to assembleFruData");
133725680e3dSMarc Olberding             return false;
133825680e3dSMarc Olberding         }
133925680e3dSMarc Olberding     }
134025680e3dSMarc Olberding 
1341cf28896cSNaresh Solanki     // Clear the existing FRU data
1342cf28896cSNaresh Solanki     fruData.clear();
1343cf28896cSNaresh Solanki     fruData.resize(8); // Start with the header size
1344cf28896cSNaresh Solanki 
1345cf28896cSNaresh Solanki     // Write the header
1346cf28896cSNaresh Solanki     fruData[0] = fruVersion; // Version
1347cf28896cSNaresh Solanki     fruData[1] = 0;          // Internal area offset
1348cf28896cSNaresh Solanki     fruData[2] = 0;          // Chassis area offset
1349cf28896cSNaresh Solanki     fruData[3] = 0;          // Board area offset
1350cf28896cSNaresh Solanki     fruData[4] = 0;          // Product area offset
1351cf28896cSNaresh Solanki     fruData[5] = 0;          // Multirecord area offset
1352cf28896cSNaresh Solanki     fruData[6] = 0;          // Pad
1353cf28896cSNaresh Solanki     fruData[7] = 0;          // Checksum (to be updated later)
1354cf28896cSNaresh Solanki 
1355cf28896cSNaresh Solanki     size_t writeOffset = 8;  // Start writing after the header
1356cf28896cSNaresh Solanki 
1357cf28896cSNaresh Solanki     for (fruAreas area = fruAreas::fruAreaInternal;
1358cf28896cSNaresh Solanki          area <= fruAreas::fruAreaMultirecord; ++area)
1359cf28896cSNaresh Solanki     {
136025680e3dSMarc Olberding         const auto& areaBytes = areasData[static_cast<size_t>(area)];
1361cf28896cSNaresh Solanki 
136225680e3dSMarc Olberding         if (areaBytes.empty())
1363cf28896cSNaresh Solanki         {
1364cf28896cSNaresh Solanki             lg2::debug("Skipping empty area: {AREA}", "AREA",
1365cf28896cSNaresh Solanki                        getFruAreaName(area));
1366cf28896cSNaresh Solanki             continue; // Skip areas that are not present
1367cf28896cSNaresh Solanki         }
1368cf28896cSNaresh Solanki 
1369cf28896cSNaresh Solanki         // Set the area offset in the header
137025680e3dSMarc Olberding         fruData[getHeaderAreaFieldOffset(area)] = writeOffset / fruBlockSize;
137125680e3dSMarc Olberding         fruData.append_range(areaBytes);
137225680e3dSMarc Olberding         writeOffset += areaBytes.size();
1373cf28896cSNaresh Solanki     }
1374cf28896cSNaresh Solanki 
1375cf28896cSNaresh Solanki     // Update the header checksum
137625680e3dSMarc Olberding     if (!updateHeaderChecksum(fruData))
1377cf28896cSNaresh Solanki     {
137825680e3dSMarc Olberding         lg2::error("failed to update header checksum");
1379cf28896cSNaresh Solanki         return false;
1380cf28896cSNaresh Solanki     }
138125680e3dSMarc Olberding 
1382cf28896cSNaresh Solanki     return true;
1383cf28896cSNaresh Solanki }
1384cf28896cSNaresh Solanki 
1385cf28896cSNaresh Solanki // Create a dummy area in areData variable based on specified fruArea
createDummyArea(fruAreas fruArea,std::vector<uint8_t> & areaData)1386cf28896cSNaresh Solanki bool createDummyArea(fruAreas fruArea, std::vector<uint8_t>& areaData)
1387cf28896cSNaresh Solanki {
1388cf28896cSNaresh Solanki     uint8_t numOfFields = 0;
1389cf28896cSNaresh Solanki     uint8_t numOfBlocks = 0;
1390cf28896cSNaresh Solanki     // Clear the areaData vector
1391cf28896cSNaresh Solanki     areaData.clear();
1392cf28896cSNaresh Solanki 
1393cf28896cSNaresh Solanki     // Set the version, length, and other fields
139425680e3dSMarc Olberding     areaData.push_back(fruVersion); // Version 1
1395cf28896cSNaresh Solanki     areaData.push_back(0);          // Length (to be updated later)
1396cf28896cSNaresh Solanki 
1397cf28896cSNaresh Solanki     switch (fruArea)
1398cf28896cSNaresh Solanki     {
1399cf28896cSNaresh Solanki         case fruAreas::fruAreaChassis:
1400cf28896cSNaresh Solanki             areaData.push_back(0x00); // Chassis type
1401cf28896cSNaresh Solanki             numOfFields = chassisFruAreas.size();
1402cf28896cSNaresh Solanki             break;
1403cf28896cSNaresh Solanki         case fruAreas::fruAreaBoard:
1404cf28896cSNaresh Solanki             areaData.push_back(0x00); // Board language code (default)
1405cf28896cSNaresh Solanki             areaData.insert(areaData.end(),
1406cf28896cSNaresh Solanki                             {0x00, 0x00,
1407cf28896cSNaresh Solanki                              0x00}); // Board manufacturer date (default)
1408cf28896cSNaresh Solanki             numOfFields = boardFruAreas.size();
1409cf28896cSNaresh Solanki             break;
1410cf28896cSNaresh Solanki         case fruAreas::fruAreaProduct:
1411cf28896cSNaresh Solanki             areaData.push_back(0x00); // Product language code (default)
1412cf28896cSNaresh Solanki             numOfFields = productFruAreas.size();
1413cf28896cSNaresh Solanki             break;
1414cf28896cSNaresh Solanki         default:
1415cf28896cSNaresh Solanki             lg2::debug("Invalid FRU area to create: {AREA}", "AREA",
1416cf28896cSNaresh Solanki                        static_cast<int>(fruArea));
1417cf28896cSNaresh Solanki             return false;
1418cf28896cSNaresh Solanki     }
1419cf28896cSNaresh Solanki 
1420cf28896cSNaresh Solanki     for (size_t i = 0; i < numOfFields; ++i)
1421cf28896cSNaresh Solanki     {
1422cf28896cSNaresh Solanki         areaData.push_back(0xc0); // Empty field type
1423cf28896cSNaresh Solanki     }
1424cf28896cSNaresh Solanki 
1425cf28896cSNaresh Solanki     // Add EndOfFields marker
1426cf28896cSNaresh Solanki     areaData.push_back(0xC1);
1427cf28896cSNaresh Solanki     numOfBlocks = (areaData.size() + fruBlockSize - 1) /
1428cf28896cSNaresh Solanki                   fruBlockSize; // Calculate number of blocks needed
1429cf28896cSNaresh Solanki     areaData.resize(numOfBlocks * fruBlockSize, 0); // Fill with zeros
1430cf28896cSNaresh Solanki     areaData[1] = numOfBlocks;                      // Update length field
143125680e3dSMarc Olberding     updateAreaChecksum(areaData);
1432cf28896cSNaresh Solanki 
1433cf28896cSNaresh Solanki     return true;
1434cf28896cSNaresh Solanki }
1435cf28896cSNaresh Solanki 
14363cbff97fSChristopher Meis // Iterate FruArea Names and find start and size of the fru area that contains
14373cbff97fSChristopher Meis // the propertyName and the field start location for the property. fruAreaParams
14383cbff97fSChristopher Meis // struct values fruAreaStart, fruAreaSize, fruAreaEnd, fieldLoc values gets
14393cbff97fSChristopher Meis // updated/returned if successful.
14403cbff97fSChristopher Meis 
findFruAreaLocationAndField(std::vector<uint8_t> & fruData,const std::string & propertyName,struct FruArea & fruAreaParams)14413cbff97fSChristopher Meis bool findFruAreaLocationAndField(std::vector<uint8_t>& fruData,
14423cbff97fSChristopher Meis                                  const std::string& propertyName,
14433cbff97fSChristopher Meis                                  struct FruArea& fruAreaParams)
14443cbff97fSChristopher Meis {
14453cbff97fSChristopher Meis     const std::vector<std::string>* fruAreaFieldNames = nullptr;
14463cbff97fSChristopher Meis 
14473cbff97fSChristopher Meis     uint8_t fruAreaOffsetFieldValue = 0;
14483cbff97fSChristopher Meis     size_t offset = 0;
14493cbff97fSChristopher Meis     std::string areaName = propertyName.substr(0, propertyName.find('_'));
14503cbff97fSChristopher Meis     std::string propertyNamePrefix = areaName + "_";
14513cbff97fSChristopher Meis     auto it = std::find(fruAreaNames.begin(), fruAreaNames.end(), areaName);
14523cbff97fSChristopher Meis     if (it == fruAreaNames.end())
14533cbff97fSChristopher Meis     {
14548feb0454SAlexander Hansen         lg2::error("Can't parse area name for property {PROP} ", "PROP",
14558feb0454SAlexander Hansen                    propertyName);
14563cbff97fSChristopher Meis         return false;
14573cbff97fSChristopher Meis     }
14583cbff97fSChristopher Meis     fruAreas fruAreaToUpdate = static_cast<fruAreas>(it - fruAreaNames.begin());
14593cbff97fSChristopher Meis     fruAreaOffsetFieldValue =
14603cbff97fSChristopher Meis         fruData[getHeaderAreaFieldOffset(fruAreaToUpdate)];
14613cbff97fSChristopher Meis     switch (fruAreaToUpdate)
14623cbff97fSChristopher Meis     {
14633cbff97fSChristopher Meis         case fruAreas::fruAreaChassis:
14643cbff97fSChristopher Meis             offset = 3; // chassis part number offset. Skip fixed first 3 bytes
14653cbff97fSChristopher Meis             fruAreaFieldNames = &chassisFruAreas;
14663cbff97fSChristopher Meis             break;
14673cbff97fSChristopher Meis         case fruAreas::fruAreaBoard:
14683cbff97fSChristopher Meis             offset = 6; // board manufacturer offset. Skip fixed first 6 bytes
14693cbff97fSChristopher Meis             fruAreaFieldNames = &boardFruAreas;
14703cbff97fSChristopher Meis             break;
14713cbff97fSChristopher Meis         case fruAreas::fruAreaProduct:
14723cbff97fSChristopher Meis             // Manufacturer name offset. Skip fixed first 3 product fru bytes
14733cbff97fSChristopher Meis             // i.e. version, area length and language code
14743cbff97fSChristopher Meis             offset = 3;
14753cbff97fSChristopher Meis             fruAreaFieldNames = &productFruAreas;
14763cbff97fSChristopher Meis             break;
14773cbff97fSChristopher Meis         default:
14788feb0454SAlexander Hansen             lg2::error("Invalid PropertyName {PROP}", "PROP", propertyName);
14793cbff97fSChristopher Meis             return false;
14803cbff97fSChristopher Meis     }
14813cbff97fSChristopher Meis     if (fruAreaOffsetFieldValue == 0)
14823cbff97fSChristopher Meis     {
14838feb0454SAlexander Hansen         lg2::error("FRU Area for {PROP} not present ", "PROP", propertyName);
14843cbff97fSChristopher Meis         return false;
14853cbff97fSChristopher Meis     }
14863cbff97fSChristopher Meis 
14873cbff97fSChristopher Meis     fruAreaParams.start = fruAreaOffsetFieldValue * fruBlockSize;
14883cbff97fSChristopher Meis     fruAreaParams.size = fruData[fruAreaParams.start + 1] * fruBlockSize;
14893cbff97fSChristopher Meis     fruAreaParams.end = fruAreaParams.start + fruAreaParams.size;
14903cbff97fSChristopher Meis     size_t fruDataIter = fruAreaParams.start + offset;
14913cbff97fSChristopher Meis     size_t skipToFRUUpdateField = 0;
14923cbff97fSChristopher Meis     ssize_t fieldLength = 0;
14933cbff97fSChristopher Meis 
14943cbff97fSChristopher Meis     bool found = false;
14953cbff97fSChristopher Meis     for (const auto& field : *fruAreaFieldNames)
14963cbff97fSChristopher Meis     {
14973cbff97fSChristopher Meis         skipToFRUUpdateField++;
14983cbff97fSChristopher Meis         if (propertyName == propertyNamePrefix + field)
14993cbff97fSChristopher Meis         {
15003cbff97fSChristopher Meis             found = true;
15013cbff97fSChristopher Meis             break;
15023cbff97fSChristopher Meis         }
15033cbff97fSChristopher Meis     }
15043cbff97fSChristopher Meis     if (!found)
15053cbff97fSChristopher Meis     {
15063cbff97fSChristopher Meis         std::size_t pos = propertyName.find(fruCustomFieldName);
15073cbff97fSChristopher Meis         if (pos == std::string::npos)
15083cbff97fSChristopher Meis         {
15098feb0454SAlexander Hansen             lg2::error("PropertyName doesn't exist in FRU Area Vectors: {PROP}",
15108feb0454SAlexander Hansen                        "PROP", propertyName);
15113cbff97fSChristopher Meis             return false;
15123cbff97fSChristopher Meis         }
15133cbff97fSChristopher Meis         std::string fieldNumStr =
15143cbff97fSChristopher Meis             propertyName.substr(pos + fruCustomFieldName.length());
15153cbff97fSChristopher Meis         size_t fieldNum = std::stoi(fieldNumStr);
15163cbff97fSChristopher Meis         if (fieldNum == 0)
15173cbff97fSChristopher Meis         {
15188feb0454SAlexander Hansen             lg2::error("PropertyName not recognized: {PROP}", "PROP",
15198feb0454SAlexander Hansen                        propertyName);
15203cbff97fSChristopher Meis             return false;
15213cbff97fSChristopher Meis         }
15223cbff97fSChristopher Meis         skipToFRUUpdateField += fieldNum;
15233cbff97fSChristopher Meis     }
15243cbff97fSChristopher Meis 
15253cbff97fSChristopher Meis     for (size_t i = 1; i < skipToFRUUpdateField; i++)
15263cbff97fSChristopher Meis     {
15273cbff97fSChristopher Meis         if (fruDataIter < fruData.size())
15283cbff97fSChristopher Meis         {
15293cbff97fSChristopher Meis             fieldLength = getFieldLength(fruData[fruDataIter]);
15303cbff97fSChristopher Meis 
15313cbff97fSChristopher Meis             if (fieldLength < 0)
15323cbff97fSChristopher Meis             {
15333cbff97fSChristopher Meis                 break;
15343cbff97fSChristopher Meis             }
15353cbff97fSChristopher Meis             fruDataIter += 1 + fieldLength;
15363cbff97fSChristopher Meis         }
15373cbff97fSChristopher Meis     }
15383cbff97fSChristopher Meis     fruAreaParams.updateFieldLoc = fruDataIter;
15393cbff97fSChristopher Meis 
15403cbff97fSChristopher Meis     return true;
15413cbff97fSChristopher Meis }
15423cbff97fSChristopher Meis 
15433cbff97fSChristopher Meis // Copy the FRU Area fields and properties into restFRUAreaFieldsData vector.
15443cbff97fSChristopher Meis // Return true for success and false for failure.
15453cbff97fSChristopher Meis 
copyRestFRUArea(std::vector<uint8_t> & fruData,const std::string & propertyName,struct FruArea & fruAreaParams,std::vector<uint8_t> & restFRUAreaFieldsData)15463cbff97fSChristopher Meis bool copyRestFRUArea(std::vector<uint8_t>& fruData,
15473cbff97fSChristopher Meis                      const std::string& propertyName,
15483cbff97fSChristopher Meis                      struct FruArea& fruAreaParams,
15493cbff97fSChristopher Meis                      std::vector<uint8_t>& restFRUAreaFieldsData)
15503cbff97fSChristopher Meis {
15513cbff97fSChristopher Meis     size_t fieldLoc = fruAreaParams.updateFieldLoc;
15523cbff97fSChristopher Meis     size_t start = fruAreaParams.start;
15533cbff97fSChristopher Meis     size_t fruAreaSize = fruAreaParams.size;
15543cbff97fSChristopher Meis 
15553cbff97fSChristopher Meis     // Push post update fru field bytes to a vector
15563cbff97fSChristopher Meis     ssize_t fieldLength = getFieldLength(fruData[fieldLoc]);
15573cbff97fSChristopher Meis     if (fieldLength < 0)
15583cbff97fSChristopher Meis     {
15598feb0454SAlexander Hansen         lg2::error("Property {PROP} not present ", "PROP", propertyName);
15603cbff97fSChristopher Meis         return false;
15613cbff97fSChristopher Meis     }
15623cbff97fSChristopher Meis 
15633cbff97fSChristopher Meis     size_t fruDataIter = 0;
15643cbff97fSChristopher Meis     fruDataIter = fieldLoc;
15653cbff97fSChristopher Meis     fruDataIter += 1 + fieldLength;
15663cbff97fSChristopher Meis     size_t restFRUFieldsLoc = fruDataIter;
15673cbff97fSChristopher Meis     size_t endOfFieldsLoc = 0;
15683cbff97fSChristopher Meis 
15693cbff97fSChristopher Meis     if (fruDataIter < fruData.size())
15703cbff97fSChristopher Meis     {
15713cbff97fSChristopher Meis         while ((fieldLength = getFieldLength(fruData[fruDataIter])) >= 0)
15723cbff97fSChristopher Meis         {
15733cbff97fSChristopher Meis             if (fruDataIter >= (start + fruAreaSize))
15743cbff97fSChristopher Meis             {
15753cbff97fSChristopher Meis                 fruDataIter = start + fruAreaSize;
15763cbff97fSChristopher Meis                 break;
15773cbff97fSChristopher Meis             }
15783cbff97fSChristopher Meis             fruDataIter += 1 + fieldLength;
15793cbff97fSChristopher Meis         }
15803cbff97fSChristopher Meis         endOfFieldsLoc = fruDataIter;
15813cbff97fSChristopher Meis     }
15823cbff97fSChristopher Meis 
15833cbff97fSChristopher Meis     std::copy_n(fruData.begin() + restFRUFieldsLoc,
15843cbff97fSChristopher Meis                 endOfFieldsLoc - restFRUFieldsLoc + 1,
15853cbff97fSChristopher Meis                 std::back_inserter(restFRUAreaFieldsData));
15863cbff97fSChristopher Meis 
15873cbff97fSChristopher Meis     fruAreaParams.restFieldsLoc = restFRUFieldsLoc;
15883cbff97fSChristopher Meis     fruAreaParams.restFieldsEnd = endOfFieldsLoc;
15893cbff97fSChristopher Meis 
15903cbff97fSChristopher Meis     return true;
15913cbff97fSChristopher Meis }
15923cbff97fSChristopher Meis 
15933cbff97fSChristopher Meis // Get all device dbus path and match path with product name using
15943cbff97fSChristopher Meis // regular expression and find the device index for all devices.
15953cbff97fSChristopher Meis 
findIndexForFRU(std::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,std::string & productName)15963cbff97fSChristopher Meis std::optional<int> findIndexForFRU(
1597*dbf95b2cSEd Tanous     std::flat_map<std::pair<size_t, size_t>,
1598*dbf95b2cSEd Tanous                   std::shared_ptr<sdbusplus::asio::dbus_interface>>&
1599*dbf95b2cSEd Tanous         dbusInterfaceMap,
16003cbff97fSChristopher Meis     std::string& productName)
16013cbff97fSChristopher Meis {
16023cbff97fSChristopher Meis     int highest = -1;
16033cbff97fSChristopher Meis     bool found = false;
16043cbff97fSChristopher Meis 
16053cbff97fSChristopher Meis     for (const auto& busIface : dbusInterfaceMap)
16063cbff97fSChristopher Meis     {
16073cbff97fSChristopher Meis         std::string path = busIface.second->get_object_path();
16083cbff97fSChristopher Meis         if (std::regex_match(path, std::regex(productName + "(_\\d+|)$")))
16093cbff97fSChristopher Meis         {
16103cbff97fSChristopher Meis             // Check if the match named has extra information.
16113cbff97fSChristopher Meis             found = true;
16123cbff97fSChristopher Meis             std::smatch baseMatch;
16133cbff97fSChristopher Meis 
16143cbff97fSChristopher Meis             bool match = std::regex_match(path, baseMatch,
16153cbff97fSChristopher Meis                                           std::regex(productName + "_(\\d+)$"));
16163cbff97fSChristopher Meis             if (match)
16173cbff97fSChristopher Meis             {
16183cbff97fSChristopher Meis                 if (baseMatch.size() == 2)
16193cbff97fSChristopher Meis                 {
16203cbff97fSChristopher Meis                     std::ssub_match baseSubMatch = baseMatch[1];
16213cbff97fSChristopher Meis                     std::string base = baseSubMatch.str();
16223cbff97fSChristopher Meis 
16233cbff97fSChristopher Meis                     int value = std::stoi(base);
16243cbff97fSChristopher Meis                     highest = (value > highest) ? value : highest;
16253cbff97fSChristopher Meis                 }
16263cbff97fSChristopher Meis             }
16273cbff97fSChristopher Meis         }
16283cbff97fSChristopher Meis     } // end searching objects
16293cbff97fSChristopher Meis 
16303cbff97fSChristopher Meis     if (!found)
16313cbff97fSChristopher Meis     {
16323cbff97fSChristopher Meis         return std::nullopt;
16333cbff97fSChristopher Meis     }
16343cbff97fSChristopher Meis     return highest;
16353cbff97fSChristopher Meis }
16363cbff97fSChristopher Meis 
16373cbff97fSChristopher Meis // This function does format fru data as per IPMI format and find the
16383cbff97fSChristopher Meis // productName in the formatted fru data, get that productName and return
16393cbff97fSChristopher Meis // productName if found or return NULL.
16403cbff97fSChristopher Meis 
getProductName(std::vector<uint8_t> & device,std::flat_map<std::string,std::string,std::less<>> & formattedFRU,uint32_t bus,uint32_t address,size_t & unknownBusObjectCount)16413cbff97fSChristopher Meis std::optional<std::string> getProductName(
16423cbff97fSChristopher Meis     std::vector<uint8_t>& device,
1643*dbf95b2cSEd Tanous     std::flat_map<std::string, std::string, std::less<>>& formattedFRU,
16443cbff97fSChristopher Meis     uint32_t bus, uint32_t address, size_t& unknownBusObjectCount)
16453cbff97fSChristopher Meis {
16463cbff97fSChristopher Meis     std::string productName;
16473cbff97fSChristopher Meis 
16483cbff97fSChristopher Meis     resCodes res = formatIPMIFRU(device, formattedFRU);
16493cbff97fSChristopher Meis     if (res == resCodes::resErr)
16503cbff97fSChristopher Meis     {
16518feb0454SAlexander Hansen         lg2::error("failed to parse FRU for device at bus {BUS} address {ADDR}",
16528feb0454SAlexander Hansen                    "BUS", bus, "ADDR", address);
16533cbff97fSChristopher Meis         return std::nullopt;
16543cbff97fSChristopher Meis     }
16553cbff97fSChristopher Meis     if (res == resCodes::resWarn)
16563cbff97fSChristopher Meis     {
16578feb0454SAlexander Hansen         lg2::error(
16588feb0454SAlexander Hansen             "Warnings while parsing FRU for device at bus {BUS} address {ADDR}",
16598feb0454SAlexander Hansen             "BUS", bus, "ADDR", address);
16603cbff97fSChristopher Meis     }
16613cbff97fSChristopher Meis 
16623cbff97fSChristopher Meis     auto productNameFind = formattedFRU.find("BOARD_PRODUCT_NAME");
16633cbff97fSChristopher Meis     // Not found under Board section or an empty string.
16643cbff97fSChristopher Meis     if (productNameFind == formattedFRU.end() ||
16653cbff97fSChristopher Meis         productNameFind->second.empty())
16663cbff97fSChristopher Meis     {
16673cbff97fSChristopher Meis         productNameFind = formattedFRU.find("PRODUCT_PRODUCT_NAME");
16683cbff97fSChristopher Meis     }
16693cbff97fSChristopher Meis     // Found under Product section and not an empty string.
16703cbff97fSChristopher Meis     if (productNameFind != formattedFRU.end() &&
16713cbff97fSChristopher Meis         !productNameFind->second.empty())
16723cbff97fSChristopher Meis     {
16733cbff97fSChristopher Meis         productName = productNameFind->second;
16743cbff97fSChristopher Meis         std::regex illegalObject("[^A-Za-z0-9_]");
16753cbff97fSChristopher Meis         productName = std::regex_replace(productName, illegalObject, "_");
16763cbff97fSChristopher Meis     }
16773cbff97fSChristopher Meis     else
16783cbff97fSChristopher Meis     {
16793cbff97fSChristopher Meis         productName = "UNKNOWN" + std::to_string(unknownBusObjectCount);
16803cbff97fSChristopher Meis         unknownBusObjectCount++;
16813cbff97fSChristopher Meis     }
16823cbff97fSChristopher Meis     return productName;
16833cbff97fSChristopher Meis }
16843cbff97fSChristopher Meis 
getFruData(std::vector<uint8_t> & fruData,uint32_t bus,uint32_t address)16853cbff97fSChristopher Meis bool getFruData(std::vector<uint8_t>& fruData, uint32_t bus, uint32_t address)
16863cbff97fSChristopher Meis {
16873cbff97fSChristopher Meis     try
16883cbff97fSChristopher Meis     {
16893cbff97fSChristopher Meis         fruData = getFRUInfo(static_cast<uint16_t>(bus),
16903cbff97fSChristopher Meis                              static_cast<uint8_t>(address));
16913cbff97fSChristopher Meis     }
16923cbff97fSChristopher Meis     catch (const std::invalid_argument& e)
16933cbff97fSChristopher Meis     {
16948feb0454SAlexander Hansen         lg2::error("Failure getting FRU Info: {ERR}", "ERR", e);
16953cbff97fSChristopher Meis         return false;
16963cbff97fSChristopher Meis     }
16973cbff97fSChristopher Meis 
16983cbff97fSChristopher Meis     return !fruData.empty();
16993cbff97fSChristopher Meis }
170089092a9cSNaresh Solanki 
isFieldEditable(std::string_view fieldName)170189092a9cSNaresh Solanki bool isFieldEditable(std::string_view fieldName)
170289092a9cSNaresh Solanki {
170389092a9cSNaresh Solanki     if (fieldName == "PRODUCT_ASSET_TAG")
170489092a9cSNaresh Solanki     {
170589092a9cSNaresh Solanki         return true; // PRODUCT_ASSET_TAG is always editable.
170689092a9cSNaresh Solanki     }
170789092a9cSNaresh Solanki 
170889092a9cSNaresh Solanki     if (!ENABLE_FRU_UPDATE_PROPERTY)
170989092a9cSNaresh Solanki     {
171089092a9cSNaresh Solanki         return false; // If FRU update is disabled, no fields are editable.
171189092a9cSNaresh Solanki     }
171289092a9cSNaresh Solanki 
171389092a9cSNaresh Solanki     // Editable fields
171489092a9cSNaresh Solanki     constexpr std::array<std::string_view, 8> editableFields = {
171589092a9cSNaresh Solanki         "MANUFACTURER",  "PRODUCT_NAME", "PART_NUMBER",    "VERSION",
171689092a9cSNaresh Solanki         "SERIAL_NUMBER", "ASSET_TAG",    "FRU_VERSION_ID", "INFO_AM"};
171789092a9cSNaresh Solanki 
171889092a9cSNaresh Solanki     // Find position of first underscore
171989092a9cSNaresh Solanki     std::size_t pos = fieldName.find('_');
172089092a9cSNaresh Solanki     if (pos == std::string_view::npos || pos + 1 >= fieldName.size())
172189092a9cSNaresh Solanki     {
172289092a9cSNaresh Solanki         return false;
172389092a9cSNaresh Solanki     }
172489092a9cSNaresh Solanki 
172589092a9cSNaresh Solanki     // Extract substring after the underscore
172689092a9cSNaresh Solanki     std::string_view subField = fieldName.substr(pos + 1);
172789092a9cSNaresh Solanki 
172889092a9cSNaresh Solanki     // Trim trailing digits
172989092a9cSNaresh Solanki     while (!subField.empty() && (std::isdigit(subField.back()) != 0))
173089092a9cSNaresh Solanki     {
173189092a9cSNaresh Solanki         subField.remove_suffix(1);
173289092a9cSNaresh Solanki     }
173389092a9cSNaresh Solanki 
173489092a9cSNaresh Solanki     // Match against editable fields
173589092a9cSNaresh Solanki     return std::ranges::contains(editableFields, subField);
173689092a9cSNaresh Solanki }
17379bde15b6SMarc Olberding 
updateAddProperty(const std::string & propertyValue,const std::string & propertyName,std::vector<uint8_t> & fruData)17389bde15b6SMarc Olberding bool updateAddProperty(const std::string& propertyValue,
17399bde15b6SMarc Olberding                        const std::string& propertyName,
17409bde15b6SMarc Olberding                        std::vector<uint8_t>& fruData)
17419bde15b6SMarc Olberding {
17429bde15b6SMarc Olberding     // Validate field length: must be 2–63 characters
17439bde15b6SMarc Olberding     const size_t len = propertyValue.length();
17449bde15b6SMarc Olberding     if (len == 1 || len > 63)
17459bde15b6SMarc Olberding     {
17469bde15b6SMarc Olberding         lg2::error(
17479bde15b6SMarc Olberding             "FRU field data must be 0 or between 2 and 63 characters. Invalid Length: {LEN}",
17489bde15b6SMarc Olberding             "LEN", len);
17499bde15b6SMarc Olberding         return false;
17509bde15b6SMarc Olberding     }
17519bde15b6SMarc Olberding 
17529bde15b6SMarc Olberding     if (fruData.empty())
17539bde15b6SMarc Olberding     {
17549bde15b6SMarc Olberding         lg2::error("Empty FRU data\n");
17559bde15b6SMarc Olberding         return false;
17569bde15b6SMarc Olberding     }
17579bde15b6SMarc Olberding 
17589bde15b6SMarc Olberding     // Extract area name (prefix before underscore)
17599bde15b6SMarc Olberding     std::string areaName = propertyName.substr(0, propertyName.find('_'));
17609bde15b6SMarc Olberding     auto areaIterator =
17619bde15b6SMarc Olberding         std::find(fruAreaNames.begin(), fruAreaNames.end(), areaName);
17629bde15b6SMarc Olberding     if (areaIterator == fruAreaNames.end())
17639bde15b6SMarc Olberding     {
17649bde15b6SMarc Olberding         lg2::error("Failed to get FRU area for property: {AREA}", "AREA",
17659bde15b6SMarc Olberding                    areaName);
17669bde15b6SMarc Olberding         return false;
17679bde15b6SMarc Olberding     }
17689bde15b6SMarc Olberding 
17699bde15b6SMarc Olberding     fruAreas fruAreaToUpdate = static_cast<fruAreas>(
17709bde15b6SMarc Olberding         std::distance(fruAreaNames.begin(), areaIterator));
17719bde15b6SMarc Olberding 
17729bde15b6SMarc Olberding     std::vector<std::vector<uint8_t>> areasData;
17739bde15b6SMarc Olberding     if (!disassembleFruData(fruData, areasData))
17749bde15b6SMarc Olberding     {
17759bde15b6SMarc Olberding         lg2::error("Failed to disassemble Fru Data");
17769bde15b6SMarc Olberding         return false;
17779bde15b6SMarc Olberding     }
17789bde15b6SMarc Olberding 
17799bde15b6SMarc Olberding     std::vector<uint8_t>& areaData =
17809bde15b6SMarc Olberding         areasData[static_cast<size_t>(fruAreaToUpdate)];
17819bde15b6SMarc Olberding     if (areaData.empty())
17829bde15b6SMarc Olberding     {
17839bde15b6SMarc Olberding         // If ENABLE_FRU_AREA_RESIZE is not defined then return with failure
17849bde15b6SMarc Olberding #ifndef ENABLE_FRU_AREA_RESIZE
17859bde15b6SMarc Olberding         lg2::error(
17869bde15b6SMarc Olberding             "FRU area {AREA} not present and ENABLE_FRU_AREA_RESIZE is not set. "
17879bde15b6SMarc Olberding             "Returning failure.",
17889bde15b6SMarc Olberding             "AREA", areaName);
17899bde15b6SMarc Olberding         return false;
17909bde15b6SMarc Olberding #endif
17919bde15b6SMarc Olberding         if (!createDummyArea(fruAreaToUpdate, areaData))
17929bde15b6SMarc Olberding         {
17939bde15b6SMarc Olberding             lg2::error("Failed to create dummy area for {AREA}", "AREA",
17949bde15b6SMarc Olberding                        areaName);
17959bde15b6SMarc Olberding             return false;
17969bde15b6SMarc Olberding         }
17979bde15b6SMarc Olberding     }
17989bde15b6SMarc Olberding 
17999bde15b6SMarc Olberding     if (!setField(fruAreaToUpdate, areaData, propertyName, propertyValue))
18009bde15b6SMarc Olberding     {
18019bde15b6SMarc Olberding         lg2::error("Failed to set field value for property: {PROPERTY}",
18029bde15b6SMarc Olberding                    "PROPERTY", propertyName);
18039bde15b6SMarc Olberding         return false;
18049bde15b6SMarc Olberding     }
18059bde15b6SMarc Olberding 
18069bde15b6SMarc Olberding     if (!assembleFruData(fruData, areasData))
18079bde15b6SMarc Olberding     {
18089bde15b6SMarc Olberding         lg2::error("Failed to reassemble FRU data");
18099bde15b6SMarc Olberding         return false;
18109bde15b6SMarc Olberding     }
18119bde15b6SMarc Olberding 
18129bde15b6SMarc Olberding     if (fruData.empty())
18139bde15b6SMarc Olberding     {
18149bde15b6SMarc Olberding         lg2::error("FRU data is empty after assembly");
18159bde15b6SMarc Olberding         return false;
18169bde15b6SMarc Olberding     }
18179bde15b6SMarc Olberding 
18189bde15b6SMarc Olberding     return true;
18199bde15b6SMarc Olberding }
1820