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