xref: /openbmc/entity-manager/src/fru_utils.cpp (revision 5a80703cab896fcf5de2d5ed111dd4d1ab589709)
1e45d8c71SBrad Bishop /*
2e45d8c71SBrad Bishop // Copyright (c) 2018 Intel Corporation
3e45d8c71SBrad Bishop //
4e45d8c71SBrad Bishop // Licensed under the Apache License, Version 2.0 (the "License");
5e45d8c71SBrad Bishop // you may not use this file except in compliance with the License.
6e45d8c71SBrad Bishop // You may obtain a copy of the License at
7e45d8c71SBrad Bishop //
8e45d8c71SBrad Bishop //      http://www.apache.org/licenses/LICENSE-2.0
9e45d8c71SBrad Bishop //
10e45d8c71SBrad Bishop // Unless required by applicable law or agreed to in writing, software
11e45d8c71SBrad Bishop // distributed under the License is distributed on an "AS IS" BASIS,
12e45d8c71SBrad Bishop // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e45d8c71SBrad Bishop // See the License for the specific language governing permissions and
14e45d8c71SBrad Bishop // limitations under the License.
15e45d8c71SBrad Bishop */
16e45d8c71SBrad Bishop /// \file fru_utils.cpp
17e45d8c71SBrad Bishop 
18e45d8c71SBrad Bishop #include "fru_utils.hpp"
19e45d8c71SBrad Bishop 
20c3db2c3cSAlexander Hansen #include <phosphor-logging/lg2.hpp>
21c3db2c3cSAlexander Hansen 
22e45d8c71SBrad Bishop #include <array>
233013fb49SEd Tanous #include <cstddef>
24e45d8c71SBrad Bishop #include <cstdint>
25e45d8c71SBrad Bishop #include <filesystem>
265286afe2SHieu Huynh #include <iomanip>
27e45d8c71SBrad Bishop #include <iostream>
28e45d8c71SBrad Bishop #include <numeric>
29e45d8c71SBrad Bishop #include <set>
305286afe2SHieu Huynh #include <sstream>
31e45d8c71SBrad Bishop #include <string>
32e45d8c71SBrad Bishop #include <vector>
33e45d8c71SBrad Bishop 
34e45d8c71SBrad Bishop extern "C"
35e45d8c71SBrad Bishop {
36e45d8c71SBrad Bishop // Include for I2C_SMBUS_BLOCK_MAX
37e45d8c71SBrad Bishop #include <linux/i2c.h>
38e45d8c71SBrad Bishop }
39e45d8c71SBrad Bishop 
40e45d8c71SBrad Bishop constexpr size_t fruVersion = 1; // Current FRU spec version number is 1
41e45d8c71SBrad Bishop 
intelEpoch()42a3ca14a6SDelphine CC Chiu std::tm intelEpoch()
43e45d8c71SBrad Bishop {
44e45d8c71SBrad Bishop     std::tm val = {};
45e45d8c71SBrad Bishop     val.tm_year = 1996 - 1900;
46e45d8c71SBrad Bishop     val.tm_mday = 1;
47e45d8c71SBrad Bishop     return val;
48e45d8c71SBrad Bishop }
49e45d8c71SBrad Bishop 
sixBitToChar(uint8_t val)50e45d8c71SBrad Bishop char sixBitToChar(uint8_t val)
51e45d8c71SBrad Bishop {
52e45d8c71SBrad Bishop     return static_cast<char>((val & 0x3f) + ' ');
53e45d8c71SBrad Bishop }
54e45d8c71SBrad Bishop 
bcdPlusToChar(uint8_t val)55e45d8c71SBrad Bishop char bcdPlusToChar(uint8_t val)
56e45d8c71SBrad Bishop {
57e45d8c71SBrad Bishop     val &= 0xf;
58e45d8c71SBrad Bishop     return (val < 10) ? static_cast<char>(val + '0') : bcdHighChars[val - 10];
59e45d8c71SBrad Bishop }
60e45d8c71SBrad Bishop 
61e45d8c71SBrad Bishop enum FRUDataEncoding
62e45d8c71SBrad Bishop {
63e45d8c71SBrad Bishop     binary = 0x0,
64e45d8c71SBrad Bishop     bcdPlus = 0x1,
65e45d8c71SBrad Bishop     sixBitASCII = 0x2,
66e45d8c71SBrad Bishop     languageDependent = 0x3,
67e45d8c71SBrad Bishop };
68e45d8c71SBrad Bishop 
695286afe2SHieu Huynh enum MultiRecordType : uint8_t
705286afe2SHieu Huynh {
715286afe2SHieu Huynh     powerSupplyInfo = 0x00,
725286afe2SHieu Huynh     dcOutput = 0x01,
735286afe2SHieu Huynh     dcLoad = 0x02,
745286afe2SHieu Huynh     managementAccessRecord = 0x03,
755286afe2SHieu Huynh     baseCompatibilityRecord = 0x04,
765286afe2SHieu Huynh     extendedCompatibilityRecord = 0x05,
775286afe2SHieu Huynh     resvASFSMBusDeviceRecord = 0x06,
785286afe2SHieu Huynh     resvASFLegacyDeviceAlerts = 0x07,
795286afe2SHieu Huynh     resvASFRemoteControl = 0x08,
805286afe2SHieu Huynh     extendedDCOutput = 0x09,
815286afe2SHieu Huynh     extendedDCLoad = 0x0A
825286afe2SHieu Huynh };
835286afe2SHieu Huynh 
845286afe2SHieu Huynh enum SubManagementAccessRecord : uint8_t
855286afe2SHieu Huynh {
865286afe2SHieu Huynh     systemManagementURL = 0x01,
875286afe2SHieu Huynh     systemName = 0x02,
885286afe2SHieu Huynh     systemPingAddress = 0x03,
895286afe2SHieu Huynh     componentManagementURL = 0x04,
905286afe2SHieu Huynh     componentName = 0x05,
915286afe2SHieu Huynh     componentPingAddress = 0x06,
925286afe2SHieu Huynh     systemUniqueID = 0x07
935286afe2SHieu Huynh };
945286afe2SHieu Huynh 
95e45d8c71SBrad Bishop /* Decode FRU data into a std::string, given an input iterator and end. If the
96e45d8c71SBrad Bishop  * state returned is fruDataOk, then the resulting string is the decoded FRU
97e45d8c71SBrad Bishop  * data. The input iterator is advanced past the data consumed.
98e45d8c71SBrad Bishop  *
99e45d8c71SBrad Bishop  * On fruDataErr, we have lost synchronisation with the length bytes, so the
100e45d8c71SBrad Bishop  * iterator is no longer usable.
101e45d8c71SBrad Bishop  */
decodeFRUData(std::vector<uint8_t>::const_iterator & iter,const std::vector<uint8_t>::const_iterator & end,bool isLangEng)102b7077437SPatrick Williams std::pair<DecodeState, std::string> decodeFRUData(
103b7077437SPatrick Williams     std::vector<uint8_t>::const_iterator& iter,
104b7077437SPatrick Williams     const std::vector<uint8_t>::const_iterator& end, bool isLangEng)
105e45d8c71SBrad Bishop {
106e45d8c71SBrad Bishop     std::string value;
1073013fb49SEd Tanous     unsigned int i = 0;
108e45d8c71SBrad Bishop 
109e45d8c71SBrad Bishop     /* we need at least one byte to decode the type/len header */
110e45d8c71SBrad Bishop     if (iter == end)
111e45d8c71SBrad Bishop     {
112e45d8c71SBrad Bishop         std::cerr << "Truncated FRU data\n";
113e45d8c71SBrad Bishop         return make_pair(DecodeState::err, value);
114e45d8c71SBrad Bishop     }
115e45d8c71SBrad Bishop 
116e45d8c71SBrad Bishop     uint8_t c = *(iter++);
117e45d8c71SBrad Bishop 
118e45d8c71SBrad Bishop     /* 0xc1 is the end marker */
119e45d8c71SBrad Bishop     if (c == 0xc1)
120e45d8c71SBrad Bishop     {
121e45d8c71SBrad Bishop         return make_pair(DecodeState::end, value);
122e45d8c71SBrad Bishop     }
123e45d8c71SBrad Bishop 
124e45d8c71SBrad Bishop     /* decode type/len byte */
125e45d8c71SBrad Bishop     uint8_t type = static_cast<uint8_t>(c >> 6);
126e45d8c71SBrad Bishop     uint8_t len = static_cast<uint8_t>(c & 0x3f);
127e45d8c71SBrad Bishop 
128e45d8c71SBrad Bishop     /* we should have at least len bytes of data available overall */
129e45d8c71SBrad Bishop     if (iter + len > end)
130e45d8c71SBrad Bishop     {
131e45d8c71SBrad Bishop         std::cerr << "FRU data field extends past end of FRU area data\n";
132e45d8c71SBrad Bishop         return make_pair(DecodeState::err, value);
133e45d8c71SBrad Bishop     }
134e45d8c71SBrad Bishop 
135e45d8c71SBrad Bishop     switch (type)
136e45d8c71SBrad Bishop     {
137e45d8c71SBrad Bishop         case FRUDataEncoding::binary:
138e45d8c71SBrad Bishop         {
139e45d8c71SBrad Bishop             std::stringstream ss;
140e45d8c71SBrad Bishop             ss << std::hex << std::setfill('0');
141e45d8c71SBrad Bishop             for (i = 0; i < len; i++, iter++)
142e45d8c71SBrad Bishop             {
143e45d8c71SBrad Bishop                 uint8_t val = static_cast<uint8_t>(*iter);
144e45d8c71SBrad Bishop                 ss << std::setw(2) << static_cast<int>(val);
145e45d8c71SBrad Bishop             }
146e45d8c71SBrad Bishop             value = ss.str();
147e45d8c71SBrad Bishop             break;
148e45d8c71SBrad Bishop         }
149e45d8c71SBrad Bishop         case FRUDataEncoding::languageDependent:
150e45d8c71SBrad Bishop             /* For language-code dependent encodings, assume 8-bit ASCII */
151e45d8c71SBrad Bishop             value = std::string(iter, iter + len);
152e45d8c71SBrad Bishop             iter += len;
153e45d8c71SBrad Bishop 
154e45d8c71SBrad Bishop             /* English text is encoded in 8-bit ASCII + Latin 1. All other
155e45d8c71SBrad Bishop              * languages are required to use 2-byte unicode. FruDevice does not
156e45d8c71SBrad Bishop              * handle unicode.
157e45d8c71SBrad Bishop              */
158e45d8c71SBrad Bishop             if (!isLangEng)
159e45d8c71SBrad Bishop             {
160e45d8c71SBrad Bishop                 std::cerr << "Error: Non english string is not supported \n";
161e45d8c71SBrad Bishop                 return make_pair(DecodeState::err, value);
162e45d8c71SBrad Bishop             }
163e45d8c71SBrad Bishop 
164e45d8c71SBrad Bishop             break;
165e45d8c71SBrad Bishop 
166e45d8c71SBrad Bishop         case FRUDataEncoding::bcdPlus:
167e45d8c71SBrad Bishop             value = std::string();
168e45d8c71SBrad Bishop             for (i = 0; i < len; i++, iter++)
169e45d8c71SBrad Bishop             {
170e45d8c71SBrad Bishop                 uint8_t val = *iter;
171e45d8c71SBrad Bishop                 value.push_back(bcdPlusToChar(val >> 4));
172e45d8c71SBrad Bishop                 value.push_back(bcdPlusToChar(val & 0xf));
173e45d8c71SBrad Bishop             }
174e45d8c71SBrad Bishop             break;
175e45d8c71SBrad Bishop 
176e45d8c71SBrad Bishop         case FRUDataEncoding::sixBitASCII:
177e45d8c71SBrad Bishop         {
178e45d8c71SBrad Bishop             unsigned int accum = 0;
179e45d8c71SBrad Bishop             unsigned int accumBitLen = 0;
180e45d8c71SBrad Bishop             value = std::string();
181e45d8c71SBrad Bishop             for (i = 0; i < len; i++, iter++)
182e45d8c71SBrad Bishop             {
183e45d8c71SBrad Bishop                 accum |= *iter << accumBitLen;
184e45d8c71SBrad Bishop                 accumBitLen += 8;
185e45d8c71SBrad Bishop                 while (accumBitLen >= 6)
186e45d8c71SBrad Bishop                 {
187e45d8c71SBrad Bishop                     value.push_back(sixBitToChar(accum & 0x3f));
188e45d8c71SBrad Bishop                     accum >>= 6;
189e45d8c71SBrad Bishop                     accumBitLen -= 6;
190e45d8c71SBrad Bishop                 }
191e45d8c71SBrad Bishop             }
192e45d8c71SBrad Bishop         }
193e45d8c71SBrad Bishop         break;
194fc171428SEd Tanous 
195fc171428SEd Tanous         default:
196fc171428SEd Tanous         {
197fc171428SEd Tanous             return make_pair(DecodeState::err, value);
198fc171428SEd Tanous         }
199e45d8c71SBrad Bishop     }
200e45d8c71SBrad Bishop 
201e45d8c71SBrad Bishop     return make_pair(DecodeState::ok, value);
202e45d8c71SBrad Bishop }
203e45d8c71SBrad Bishop 
checkLangEng(uint8_t lang)204e45d8c71SBrad Bishop bool checkLangEng(uint8_t lang)
205e45d8c71SBrad Bishop {
206e45d8c71SBrad Bishop     // If Lang is not English then the encoding is defined as 2-byte UNICODE,
207e45d8c71SBrad Bishop     // but we don't support that.
2083013fb49SEd Tanous     if ((lang != 0U) && lang != 25)
209e45d8c71SBrad Bishop     {
210e45d8c71SBrad Bishop         std::cerr << "Warning: languages other than English is not "
211e45d8c71SBrad Bishop                      "supported\n";
212e45d8c71SBrad Bishop         // Return language flag as non english
213e45d8c71SBrad Bishop         return false;
214e45d8c71SBrad Bishop     }
215e45d8c71SBrad Bishop     return true;
216e45d8c71SBrad Bishop }
217e45d8c71SBrad Bishop 
218e45d8c71SBrad Bishop /* This function verifies for other offsets to check if they are not
219e45d8c71SBrad Bishop  * falling under other field area
220e45d8c71SBrad Bishop  *
221e45d8c71SBrad Bishop  * fruBytes:    Start of Fru data
222e45d8c71SBrad Bishop  * currentArea: Index of current area offset to be compared against all area
223e45d8c71SBrad Bishop  *              offset and it is a multiple of 8 bytes as per specification
224e45d8c71SBrad Bishop  * len:         Length of current area space and it is a multiple of 8 bytes
225e45d8c71SBrad Bishop  *              as per specification
226e45d8c71SBrad Bishop  */
verifyOffset(const std::vector<uint8_t> & fruBytes,fruAreas currentArea,uint8_t len)227e45d8c71SBrad Bishop bool verifyOffset(const std::vector<uint8_t>& fruBytes, fruAreas currentArea,
228e45d8c71SBrad Bishop                   uint8_t len)
229e45d8c71SBrad Bishop {
230e45d8c71SBrad Bishop     unsigned int fruBytesSize = fruBytes.size();
231e45d8c71SBrad Bishop 
232e45d8c71SBrad Bishop     // check if Fru data has at least 8 byte header
233e45d8c71SBrad Bishop     if (fruBytesSize <= fruBlockSize)
234e45d8c71SBrad Bishop     {
235e45d8c71SBrad Bishop         std::cerr << "Error: trying to parse empty FRU\n";
236e45d8c71SBrad Bishop         return false;
237e45d8c71SBrad Bishop     }
238e45d8c71SBrad Bishop 
239e45d8c71SBrad Bishop     // Check range of passed currentArea value
240e45d8c71SBrad Bishop     if (currentArea > fruAreas::fruAreaMultirecord)
241e45d8c71SBrad Bishop     {
242e45d8c71SBrad Bishop         std::cerr << "Error: Fru area is out of range\n";
243e45d8c71SBrad Bishop         return false;
244e45d8c71SBrad Bishop     }
245e45d8c71SBrad Bishop 
246e45d8c71SBrad Bishop     unsigned int currentAreaIndex = getHeaderAreaFieldOffset(currentArea);
247e45d8c71SBrad Bishop     if (currentAreaIndex > fruBytesSize)
248e45d8c71SBrad Bishop     {
249e45d8c71SBrad Bishop         std::cerr << "Error: Fru area index is out of range\n";
250e45d8c71SBrad Bishop         return false;
251e45d8c71SBrad Bishop     }
252e45d8c71SBrad Bishop 
253e45d8c71SBrad Bishop     unsigned int start = fruBytes[currentAreaIndex];
254e45d8c71SBrad Bishop     unsigned int end = start + len;
255e45d8c71SBrad Bishop 
256e45d8c71SBrad Bishop     /* Verify each offset within the range of start and end */
257e45d8c71SBrad Bishop     for (fruAreas area = fruAreas::fruAreaInternal;
258e45d8c71SBrad Bishop          area <= fruAreas::fruAreaMultirecord; ++area)
259e45d8c71SBrad Bishop     {
260e45d8c71SBrad Bishop         // skip the current offset
261e45d8c71SBrad Bishop         if (area == currentArea)
262e45d8c71SBrad Bishop         {
263e45d8c71SBrad Bishop             continue;
264e45d8c71SBrad Bishop         }
265e45d8c71SBrad Bishop 
266e45d8c71SBrad Bishop         unsigned int areaIndex = getHeaderAreaFieldOffset(area);
267e45d8c71SBrad Bishop         if (areaIndex > fruBytesSize)
268e45d8c71SBrad Bishop         {
269e45d8c71SBrad Bishop             std::cerr << "Error: Fru area index is out of range\n";
270e45d8c71SBrad Bishop             return false;
271e45d8c71SBrad Bishop         }
272e45d8c71SBrad Bishop 
273e45d8c71SBrad Bishop         unsigned int areaOffset = fruBytes[areaIndex];
274e45d8c71SBrad Bishop         // if areaOffset is 0 means this area is not available so skip
275e45d8c71SBrad Bishop         if (areaOffset == 0)
276e45d8c71SBrad Bishop         {
277e45d8c71SBrad Bishop             continue;
278e45d8c71SBrad Bishop         }
279e45d8c71SBrad Bishop 
280e45d8c71SBrad Bishop         // check for overlapping of current offset with given areaoffset
281e45d8c71SBrad Bishop         if (areaOffset == start || (areaOffset > start && areaOffset < end))
282e45d8c71SBrad Bishop         {
283e45d8c71SBrad Bishop             std::cerr << getFruAreaName(currentArea)
284e45d8c71SBrad Bishop                       << " offset is overlapping with " << getFruAreaName(area)
285e45d8c71SBrad Bishop                       << " offset\n";
286e45d8c71SBrad Bishop             return false;
287e45d8c71SBrad Bishop         }
288e45d8c71SBrad Bishop     }
289e45d8c71SBrad Bishop     return true;
290e45d8c71SBrad Bishop }
291e45d8c71SBrad Bishop 
parseMultirecordUUID(const std::vector<uint8_t> & device,boost::container::flat_map<std::string,std::string> & result)2925286afe2SHieu Huynh static void parseMultirecordUUID(
2935286afe2SHieu Huynh     const std::vector<uint8_t>& device,
2945286afe2SHieu Huynh     boost::container::flat_map<std::string, std::string>& result)
2955286afe2SHieu Huynh {
2965286afe2SHieu Huynh     constexpr size_t uuidDataLen = 16;
2975286afe2SHieu Huynh     constexpr size_t multiRecordHeaderLen = 5;
2985286afe2SHieu Huynh     /* UUID record data, plus one to skip past the sub-record type byte */
2995286afe2SHieu Huynh     constexpr size_t uuidRecordData = multiRecordHeaderLen + 1;
3005286afe2SHieu Huynh     constexpr size_t multiRecordEndOfListMask = 0x80;
3015286afe2SHieu Huynh     /* The UUID {00112233-4455-6677-8899-AABBCCDDEEFF} would thus be represented
3025286afe2SHieu Huynh      * as: 0x33 0x22 0x11 0x00 0x55 0x44 0x77 0x66 0x88 0x99 0xAA 0xBB 0xCC 0xDD
3035286afe2SHieu Huynh      * 0xEE 0xFF
3045286afe2SHieu Huynh      */
3055286afe2SHieu Huynh     const std::array<uint8_t, uuidDataLen> uuidCharOrder = {
3065286afe2SHieu Huynh         3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15};
3075286afe2SHieu Huynh     uint32_t areaOffset =
3085286afe2SHieu Huynh         device.at(getHeaderAreaFieldOffset(fruAreas::fruAreaMultirecord));
3095286afe2SHieu Huynh 
3105286afe2SHieu Huynh     if (areaOffset == 0)
3115286afe2SHieu Huynh     {
3125286afe2SHieu Huynh         return;
3135286afe2SHieu Huynh     }
3145286afe2SHieu Huynh 
3155286afe2SHieu Huynh     areaOffset *= fruBlockSize;
316b7077437SPatrick Williams     std::vector<uint8_t>::const_iterator fruBytesIter =
317b7077437SPatrick Williams         device.begin() + areaOffset;
3185286afe2SHieu Huynh 
3195286afe2SHieu Huynh     /* Verify area offset */
3205286afe2SHieu Huynh     if (!verifyOffset(device, fruAreas::fruAreaMultirecord, *fruBytesIter))
3215286afe2SHieu Huynh     {
3225286afe2SHieu Huynh         return;
3235286afe2SHieu Huynh     }
3245286afe2SHieu Huynh     while (areaOffset + uuidRecordData + uuidDataLen <= device.size())
3255286afe2SHieu Huynh     {
3265286afe2SHieu Huynh         if ((areaOffset < device.size()) &&
3275286afe2SHieu Huynh             (device[areaOffset] ==
3285286afe2SHieu Huynh              (uint8_t)MultiRecordType::managementAccessRecord))
3295286afe2SHieu Huynh         {
3305286afe2SHieu Huynh             if ((areaOffset + multiRecordHeaderLen < device.size()) &&
3315286afe2SHieu Huynh                 (device[areaOffset + multiRecordHeaderLen] ==
3325286afe2SHieu Huynh                  (uint8_t)SubManagementAccessRecord::systemUniqueID))
3335286afe2SHieu Huynh             {
3345286afe2SHieu Huynh                 /* Layout of UUID:
3355286afe2SHieu Huynh                  * source: https://www.ietf.org/rfc/rfc4122.txt
3365286afe2SHieu Huynh                  *
3375286afe2SHieu Huynh                  * UUID binary format (16 bytes):
3385286afe2SHieu Huynh                  * 4B-2B-2B-2B-6B (big endian)
3395286afe2SHieu Huynh                  *
3405286afe2SHieu Huynh                  * UUID string is 36 length of characters (36 bytes):
3415286afe2SHieu Huynh                  * 0        9    14   19   24
3425286afe2SHieu Huynh                  * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
3435286afe2SHieu Huynh                  *    be     be   be   be       be
3445286afe2SHieu Huynh                  * be means it should be converted to big endian.
3455286afe2SHieu Huynh                  */
3465286afe2SHieu Huynh                 /* Get UUID bytes to UUID string */
3475286afe2SHieu Huynh                 std::stringstream tmp;
3485286afe2SHieu Huynh                 tmp << std::hex << std::setfill('0');
3495286afe2SHieu Huynh                 for (size_t i = 0; i < uuidDataLen; i++)
3505286afe2SHieu Huynh                 {
3515286afe2SHieu Huynh                     tmp << std::setw(2)
3525286afe2SHieu Huynh                         << static_cast<uint16_t>(
3535286afe2SHieu Huynh                                device[areaOffset + uuidRecordData +
3545286afe2SHieu Huynh                                       uuidCharOrder[i]]);
3555286afe2SHieu Huynh                 }
3565286afe2SHieu Huynh                 std::string uuidStr = tmp.str();
3575286afe2SHieu Huynh                 result["MULTIRECORD_UUID"] =
3585286afe2SHieu Huynh                     uuidStr.substr(0, 8) + '-' + uuidStr.substr(8, 4) + '-' +
3595286afe2SHieu Huynh                     uuidStr.substr(12, 4) + '-' + uuidStr.substr(16, 4) + '-' +
3605286afe2SHieu Huynh                     uuidStr.substr(20, 12);
3615286afe2SHieu Huynh                 break;
3625286afe2SHieu Huynh             }
3635286afe2SHieu Huynh         }
3645286afe2SHieu Huynh         if ((device[areaOffset + 1] & multiRecordEndOfListMask) != 0)
3655286afe2SHieu Huynh         {
3665286afe2SHieu Huynh             break;
3675286afe2SHieu Huynh         }
3685286afe2SHieu Huynh         areaOffset = areaOffset + device[areaOffset + 2] + multiRecordHeaderLen;
3695286afe2SHieu Huynh     }
3705286afe2SHieu Huynh }
3715286afe2SHieu Huynh 
formatIPMIFRU(const std::vector<uint8_t> & fruBytes,boost::container::flat_map<std::string,std::string> & result)372*5a80703cSPatrick Williams resCodes formatIPMIFRU(
373*5a80703cSPatrick Williams     const std::vector<uint8_t>& fruBytes,
374e45d8c71SBrad Bishop     boost::container::flat_map<std::string, std::string>& result)
375e45d8c71SBrad Bishop {
376e45d8c71SBrad Bishop     resCodes ret = resCodes::resOK;
377e45d8c71SBrad Bishop     if (fruBytes.size() <= fruBlockSize)
378e45d8c71SBrad Bishop     {
379e45d8c71SBrad Bishop         std::cerr << "Error: trying to parse empty FRU \n";
380e45d8c71SBrad Bishop         return resCodes::resErr;
381e45d8c71SBrad Bishop     }
382e45d8c71SBrad Bishop     result["Common_Format_Version"] =
383e45d8c71SBrad Bishop         std::to_string(static_cast<int>(*fruBytes.begin()));
384e45d8c71SBrad Bishop 
3853013fb49SEd Tanous     const std::vector<std::string>* fruAreaFieldNames = nullptr;
386e45d8c71SBrad Bishop 
387e45d8c71SBrad Bishop     // Don't parse Internal and Multirecord areas
388e45d8c71SBrad Bishop     for (fruAreas area = fruAreas::fruAreaChassis;
389e45d8c71SBrad Bishop          area <= fruAreas::fruAreaProduct; ++area)
390e45d8c71SBrad Bishop     {
391e45d8c71SBrad Bishop         size_t offset = *(fruBytes.begin() + getHeaderAreaFieldOffset(area));
392e45d8c71SBrad Bishop         if (offset == 0)
393e45d8c71SBrad Bishop         {
394e45d8c71SBrad Bishop             continue;
395e45d8c71SBrad Bishop         }
396e45d8c71SBrad Bishop         offset *= fruBlockSize;
397b7077437SPatrick Williams         std::vector<uint8_t>::const_iterator fruBytesIter =
398b7077437SPatrick Williams             fruBytes.begin() + offset;
399e45d8c71SBrad Bishop         if (fruBytesIter + fruBlockSize >= fruBytes.end())
400e45d8c71SBrad Bishop         {
401e45d8c71SBrad Bishop             std::cerr << "Not enough data to parse \n";
402e45d8c71SBrad Bishop             return resCodes::resErr;
403e45d8c71SBrad Bishop         }
404e45d8c71SBrad Bishop         // check for format version 1
405e45d8c71SBrad Bishop         if (*fruBytesIter != 0x01)
406e45d8c71SBrad Bishop         {
407e45d8c71SBrad Bishop             std::cerr << "Unexpected version " << *fruBytesIter << "\n";
408e45d8c71SBrad Bishop             return resCodes::resErr;
409e45d8c71SBrad Bishop         }
410e45d8c71SBrad Bishop         ++fruBytesIter;
411e45d8c71SBrad Bishop 
412e45d8c71SBrad Bishop         /* Verify other area offset for overlap with current area by passing
413e45d8c71SBrad Bishop          * length of current area offset pointed by *fruBytesIter
414e45d8c71SBrad Bishop          */
415e45d8c71SBrad Bishop         if (!verifyOffset(fruBytes, area, *fruBytesIter))
416e45d8c71SBrad Bishop         {
417e45d8c71SBrad Bishop             return resCodes::resErr;
418e45d8c71SBrad Bishop         }
419e45d8c71SBrad Bishop 
420e45d8c71SBrad Bishop         size_t fruAreaSize = *fruBytesIter * fruBlockSize;
421e45d8c71SBrad Bishop         std::vector<uint8_t>::const_iterator fruBytesIterEndArea =
422e45d8c71SBrad Bishop             fruBytes.begin() + offset + fruAreaSize - 1;
423e45d8c71SBrad Bishop         ++fruBytesIter;
424e45d8c71SBrad Bishop 
425e45d8c71SBrad Bishop         uint8_t fruComputedChecksum =
426e45d8c71SBrad Bishop             calculateChecksum(fruBytes.begin() + offset, fruBytesIterEndArea);
427e45d8c71SBrad Bishop         if (fruComputedChecksum != *fruBytesIterEndArea)
428e45d8c71SBrad Bishop         {
429e45d8c71SBrad Bishop             std::stringstream ss;
430e45d8c71SBrad Bishop             ss << std::hex << std::setfill('0');
431e45d8c71SBrad Bishop             ss << "Checksum error in FRU area " << getFruAreaName(area) << "\n";
432e45d8c71SBrad Bishop             ss << "\tComputed checksum: 0x" << std::setw(2)
433e45d8c71SBrad Bishop                << static_cast<int>(fruComputedChecksum) << "\n";
434e45d8c71SBrad Bishop             ss << "\tThe read checksum: 0x" << std::setw(2)
435e45d8c71SBrad Bishop                << static_cast<int>(*fruBytesIterEndArea) << "\n";
436e45d8c71SBrad Bishop             std::cerr << ss.str();
437e45d8c71SBrad Bishop             ret = resCodes::resWarn;
438e45d8c71SBrad Bishop         }
439e45d8c71SBrad Bishop 
440e45d8c71SBrad Bishop         /* Set default language flag to true as Chassis Fru area are always
441e45d8c71SBrad Bishop          * encoded in English defined in Section 10 of Fru specification
442e45d8c71SBrad Bishop          */
443e45d8c71SBrad Bishop 
444e45d8c71SBrad Bishop         bool isLangEng = true;
445e45d8c71SBrad Bishop         switch (area)
446e45d8c71SBrad Bishop         {
447e45d8c71SBrad Bishop             case fruAreas::fruAreaChassis:
448e45d8c71SBrad Bishop             {
449e45d8c71SBrad Bishop                 result["CHASSIS_TYPE"] =
450e45d8c71SBrad Bishop                     std::to_string(static_cast<int>(*fruBytesIter));
451e45d8c71SBrad Bishop                 fruBytesIter += 1;
452e45d8c71SBrad Bishop                 fruAreaFieldNames = &chassisFruAreas;
453e45d8c71SBrad Bishop                 break;
454e45d8c71SBrad Bishop             }
455e45d8c71SBrad Bishop             case fruAreas::fruAreaBoard:
456e45d8c71SBrad Bishop             {
457e45d8c71SBrad Bishop                 uint8_t lang = *fruBytesIter;
458e45d8c71SBrad Bishop                 result["BOARD_LANGUAGE_CODE"] =
459e45d8c71SBrad Bishop                     std::to_string(static_cast<int>(lang));
460e45d8c71SBrad Bishop                 isLangEng = checkLangEng(lang);
461e45d8c71SBrad Bishop                 fruBytesIter += 1;
462e45d8c71SBrad Bishop 
463b7077437SPatrick Williams                 unsigned int minutes =
464b7077437SPatrick Williams                     *fruBytesIter | *(fruBytesIter + 1) << 8 |
465e45d8c71SBrad Bishop                     *(fruBytesIter + 2) << 16;
466e45d8c71SBrad Bishop                 std::tm fruTime = intelEpoch();
4673f98b5ebSWilly Tu                 std::time_t timeValue = timegm(&fruTime);
4683013fb49SEd Tanous                 timeValue += static_cast<long>(minutes) * 60;
469e45d8c71SBrad Bishop                 fruTime = *std::gmtime(&timeValue);
470e45d8c71SBrad Bishop 
471e45d8c71SBrad Bishop                 // Tue Nov 20 23:08:00 2018
4723013fb49SEd Tanous                 std::array<char, 32> timeString = {};
4733013fb49SEd Tanous                 auto bytes = std::strftime(timeString.data(), timeString.size(),
474536665b0SYi-Shum                                            "%Y%m%dT%H%M%SZ", &fruTime);
475e45d8c71SBrad Bishop                 if (bytes == 0)
476e45d8c71SBrad Bishop                 {
477e45d8c71SBrad Bishop                     std::cerr << "invalid time string encountered\n";
478e45d8c71SBrad Bishop                     return resCodes::resErr;
479e45d8c71SBrad Bishop                 }
480e45d8c71SBrad Bishop 
4813013fb49SEd Tanous                 result["BOARD_MANUFACTURE_DATE"] =
4823013fb49SEd Tanous                     std::string_view(timeString.data(), bytes);
483e45d8c71SBrad Bishop                 fruBytesIter += 3;
484e45d8c71SBrad Bishop                 fruAreaFieldNames = &boardFruAreas;
485e45d8c71SBrad Bishop                 break;
486e45d8c71SBrad Bishop             }
487e45d8c71SBrad Bishop             case fruAreas::fruAreaProduct:
488e45d8c71SBrad Bishop             {
489e45d8c71SBrad Bishop                 uint8_t lang = *fruBytesIter;
490e45d8c71SBrad Bishop                 result["PRODUCT_LANGUAGE_CODE"] =
491e45d8c71SBrad Bishop                     std::to_string(static_cast<int>(lang));
492e45d8c71SBrad Bishop                 isLangEng = checkLangEng(lang);
493e45d8c71SBrad Bishop                 fruBytesIter += 1;
494e45d8c71SBrad Bishop                 fruAreaFieldNames = &productFruAreas;
495e45d8c71SBrad Bishop                 break;
496e45d8c71SBrad Bishop             }
497e45d8c71SBrad Bishop             default:
498e45d8c71SBrad Bishop             {
499e45d8c71SBrad Bishop                 std::cerr << "Internal error: unexpected FRU area index: "
500e45d8c71SBrad Bishop                           << static_cast<int>(area) << " \n";
501e45d8c71SBrad Bishop                 return resCodes::resErr;
502e45d8c71SBrad Bishop             }
503e45d8c71SBrad Bishop         }
504e45d8c71SBrad Bishop         size_t fieldIndex = 0;
5053013fb49SEd Tanous         DecodeState state = DecodeState::ok;
506e45d8c71SBrad Bishop         do
507e45d8c71SBrad Bishop         {
508b7077437SPatrick Williams             auto res =
509b7077437SPatrick Williams                 decodeFRUData(fruBytesIter, fruBytesIterEndArea, isLangEng);
510e45d8c71SBrad Bishop             state = res.first;
511e45d8c71SBrad Bishop             std::string value = res.second;
512e45d8c71SBrad Bishop             std::string name;
513e45d8c71SBrad Bishop             if (fieldIndex < fruAreaFieldNames->size())
514e45d8c71SBrad Bishop             {
515e45d8c71SBrad Bishop                 name = std::string(getFruAreaName(area)) + "_" +
516e45d8c71SBrad Bishop                        fruAreaFieldNames->at(fieldIndex);
517e45d8c71SBrad Bishop             }
518e45d8c71SBrad Bishop             else
519e45d8c71SBrad Bishop             {
520e45d8c71SBrad Bishop                 name =
521e45d8c71SBrad Bishop                     std::string(getFruAreaName(area)) + "_" +
522e45d8c71SBrad Bishop                     fruCustomFieldName +
523e45d8c71SBrad Bishop                     std::to_string(fieldIndex - fruAreaFieldNames->size() + 1);
524e45d8c71SBrad Bishop             }
525e45d8c71SBrad Bishop 
526e45d8c71SBrad Bishop             if (state == DecodeState::ok)
527e45d8c71SBrad Bishop             {
528ae2a6f2aSChiang Brian                 // Strip non null characters and trailing spaces from the end
529e45d8c71SBrad Bishop                 value.erase(std::find_if(value.rbegin(), value.rend(),
530ae2a6f2aSChiang Brian                                          [](char ch) {
531ae2a6f2aSChiang Brian                                              return ((ch != 0) && (ch != ' '));
532ae2a6f2aSChiang Brian                                          })
533b7077437SPatrick Williams                                 .base(),
534e45d8c71SBrad Bishop                             value.end());
535e45d8c71SBrad Bishop 
536e45d8c71SBrad Bishop                 result[name] = std::move(value);
537e45d8c71SBrad Bishop                 ++fieldIndex;
538e45d8c71SBrad Bishop             }
539e45d8c71SBrad Bishop             else if (state == DecodeState::err)
540e45d8c71SBrad Bishop             {
541e45d8c71SBrad Bishop                 std::cerr << "Error while parsing " << name << "\n";
542e45d8c71SBrad Bishop                 ret = resCodes::resWarn;
543e45d8c71SBrad Bishop                 // Cancel decoding if failed to parse any of mandatory
544e45d8c71SBrad Bishop                 // fields
545e45d8c71SBrad Bishop                 if (fieldIndex < fruAreaFieldNames->size())
546e45d8c71SBrad Bishop                 {
547e45d8c71SBrad Bishop                     std::cerr << "Failed to parse mandatory field \n";
548e45d8c71SBrad Bishop                     return resCodes::resErr;
549e45d8c71SBrad Bishop                 }
550e45d8c71SBrad Bishop             }
551e45d8c71SBrad Bishop             else
552e45d8c71SBrad Bishop             {
553e45d8c71SBrad Bishop                 if (fieldIndex < fruAreaFieldNames->size())
554e45d8c71SBrad Bishop                 {
555b7077437SPatrick Williams                     std::cerr
556b7077437SPatrick Williams                         << "Mandatory fields absent in FRU area "
557b7077437SPatrick Williams                         << getFruAreaName(area) << " after " << name << "\n";
558e45d8c71SBrad Bishop                     ret = resCodes::resWarn;
559e45d8c71SBrad Bishop                 }
560e45d8c71SBrad Bishop             }
561e45d8c71SBrad Bishop         } while (state == DecodeState::ok);
562e45d8c71SBrad Bishop         for (; fruBytesIter < fruBytesIterEndArea; fruBytesIter++)
563e45d8c71SBrad Bishop         {
564e45d8c71SBrad Bishop             uint8_t c = *fruBytesIter;
5653013fb49SEd Tanous             if (c != 0U)
566e45d8c71SBrad Bishop             {
567e45d8c71SBrad Bishop                 std::cerr << "Non-zero byte after EndOfFields in FRU area "
568e45d8c71SBrad Bishop                           << getFruAreaName(area) << "\n";
569e45d8c71SBrad Bishop                 ret = resCodes::resWarn;
570e45d8c71SBrad Bishop                 break;
571e45d8c71SBrad Bishop             }
572e45d8c71SBrad Bishop         }
573e45d8c71SBrad Bishop     }
574e45d8c71SBrad Bishop 
5755286afe2SHieu Huynh     /* Parsing the Multirecord UUID */
5765286afe2SHieu Huynh     parseMultirecordUUID(fruBytes, result);
5775286afe2SHieu Huynh 
578e45d8c71SBrad Bishop     return ret;
579e45d8c71SBrad Bishop }
580e45d8c71SBrad Bishop 
581e45d8c71SBrad Bishop // Calculate new checksum for fru info area
calculateChecksum(std::vector<uint8_t>::const_iterator iter,std::vector<uint8_t>::const_iterator end)582e45d8c71SBrad Bishop uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
583e45d8c71SBrad Bishop                           std::vector<uint8_t>::const_iterator end)
584e45d8c71SBrad Bishop {
585e45d8c71SBrad Bishop     constexpr int checksumMod = 256;
586e45d8c71SBrad Bishop     uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
587e45d8c71SBrad Bishop     return (checksumMod - sum) % checksumMod;
588e45d8c71SBrad Bishop }
589e45d8c71SBrad Bishop 
calculateChecksum(std::vector<uint8_t> & fruAreaData)590e45d8c71SBrad Bishop uint8_t calculateChecksum(std::vector<uint8_t>& fruAreaData)
591e45d8c71SBrad Bishop {
592e45d8c71SBrad Bishop     return calculateChecksum(fruAreaData.begin(), fruAreaData.end());
593e45d8c71SBrad Bishop }
594e45d8c71SBrad Bishop 
595e45d8c71SBrad Bishop // Update new fru area length &
596e45d8c71SBrad Bishop // Update checksum at new checksum location
597e45d8c71SBrad Bishop // Return the offset of the area checksum byte
updateFRUAreaLenAndChecksum(std::vector<uint8_t> & fruData,size_t fruAreaStart,size_t fruAreaEndOfFieldsOffset,size_t fruAreaEndOffset)598b7077437SPatrick Williams unsigned int updateFRUAreaLenAndChecksum(
599b7077437SPatrick Williams     std::vector<uint8_t>& fruData, size_t fruAreaStart,
600b7077437SPatrick Williams     size_t fruAreaEndOfFieldsOffset, size_t fruAreaEndOffset)
601e45d8c71SBrad Bishop {
602e45d8c71SBrad Bishop     size_t traverseFRUAreaIndex = fruAreaEndOfFieldsOffset - fruAreaStart;
603e45d8c71SBrad Bishop 
604e45d8c71SBrad Bishop     // fill zeros for any remaining unused space
605e45d8c71SBrad Bishop     std::fill(fruData.begin() + fruAreaEndOfFieldsOffset,
606e45d8c71SBrad Bishop               fruData.begin() + fruAreaEndOffset, 0);
607e45d8c71SBrad Bishop 
608e45d8c71SBrad Bishop     size_t mod = traverseFRUAreaIndex % fruBlockSize;
6093013fb49SEd Tanous     size_t checksumLoc = 0;
6103013fb49SEd Tanous     if (mod == 0U)
611e45d8c71SBrad Bishop     {
612e45d8c71SBrad Bishop         traverseFRUAreaIndex += (fruBlockSize);
613e45d8c71SBrad Bishop         checksumLoc = fruAreaEndOfFieldsOffset + (fruBlockSize - 1);
614e45d8c71SBrad Bishop     }
615e45d8c71SBrad Bishop     else
616e45d8c71SBrad Bishop     {
617e45d8c71SBrad Bishop         traverseFRUAreaIndex += (fruBlockSize - mod);
618e45d8c71SBrad Bishop         checksumLoc = fruAreaEndOfFieldsOffset + (fruBlockSize - mod - 1);
619e45d8c71SBrad Bishop     }
620e45d8c71SBrad Bishop 
6213013fb49SEd Tanous     size_t newFRUAreaLen =
6223013fb49SEd Tanous         (traverseFRUAreaIndex / fruBlockSize) +
6233013fb49SEd Tanous         static_cast<unsigned long>((traverseFRUAreaIndex % fruBlockSize) != 0);
624e45d8c71SBrad Bishop     size_t fruAreaLengthLoc = fruAreaStart + 1;
625e45d8c71SBrad Bishop     fruData[fruAreaLengthLoc] = static_cast<uint8_t>(newFRUAreaLen);
626e45d8c71SBrad Bishop 
627e45d8c71SBrad Bishop     // Calculate new checksum
628e45d8c71SBrad Bishop     std::vector<uint8_t> finalFRUData;
629e45d8c71SBrad Bishop     std::copy_n(fruData.begin() + fruAreaStart, checksumLoc - fruAreaStart,
630e45d8c71SBrad Bishop                 std::back_inserter(finalFRUData));
631e45d8c71SBrad Bishop 
632e45d8c71SBrad Bishop     fruData[checksumLoc] = calculateChecksum(finalFRUData);
633e45d8c71SBrad Bishop     return checksumLoc;
634e45d8c71SBrad Bishop }
635e45d8c71SBrad Bishop 
getFieldLength(uint8_t fruFieldTypeLenValue)636e45d8c71SBrad Bishop ssize_t getFieldLength(uint8_t fruFieldTypeLenValue)
637e45d8c71SBrad Bishop {
638e45d8c71SBrad Bishop     constexpr uint8_t typeLenMask = 0x3F;
639e45d8c71SBrad Bishop     constexpr uint8_t endOfFields = 0xC1;
640e45d8c71SBrad Bishop     if (fruFieldTypeLenValue == endOfFields)
641e45d8c71SBrad Bishop     {
642e45d8c71SBrad Bishop         return -1;
643e45d8c71SBrad Bishop     }
644e45d8c71SBrad Bishop     return fruFieldTypeLenValue & typeLenMask;
645e45d8c71SBrad Bishop }
646e45d8c71SBrad Bishop 
validateHeader(const std::array<uint8_t,I2C_SMBUS_BLOCK_MAX> & blockData)647e45d8c71SBrad Bishop bool validateHeader(const std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData)
648e45d8c71SBrad Bishop {
649e45d8c71SBrad Bishop     // ipmi spec format version number is currently at 1, verify it
650e45d8c71SBrad Bishop     if (blockData[0] != fruVersion)
651e45d8c71SBrad Bishop     {
652c3db2c3cSAlexander Hansen         lg2::debug(
653c3db2c3cSAlexander Hansen             "FRU spec version {VERSION} not supported. Supported version is {SUPPORTED_VERSION}",
654c3db2c3cSAlexander Hansen             "VERSION", lg2::hex, blockData[0], "SUPPORTED_VERSION", lg2::hex,
655c3db2c3cSAlexander Hansen             fruVersion);
656e45d8c71SBrad Bishop         return false;
657e45d8c71SBrad Bishop     }
658e45d8c71SBrad Bishop 
659e45d8c71SBrad Bishop     // verify pad is set to 0
660e45d8c71SBrad Bishop     if (blockData[6] != 0x0)
661e45d8c71SBrad Bishop     {
662c3db2c3cSAlexander Hansen         lg2::debug("Pad value in header is non zero, value is {VALUE}", "VALUE",
663c3db2c3cSAlexander Hansen                    lg2::hex, blockData[6]);
664e45d8c71SBrad Bishop         return false;
665e45d8c71SBrad Bishop     }
666e45d8c71SBrad Bishop 
667e45d8c71SBrad Bishop     // verify offsets are 0, or don't point to another offset
668e45d8c71SBrad Bishop     std::set<uint8_t> foundOffsets;
669e45d8c71SBrad Bishop     for (int ii = 1; ii < 6; ii++)
670e45d8c71SBrad Bishop     {
671e45d8c71SBrad Bishop         if (blockData[ii] == 0)
672e45d8c71SBrad Bishop         {
673e45d8c71SBrad Bishop             continue;
674e45d8c71SBrad Bishop         }
675e45d8c71SBrad Bishop         auto inserted = foundOffsets.insert(blockData[ii]);
676e45d8c71SBrad Bishop         if (!inserted.second)
677e45d8c71SBrad Bishop         {
678e45d8c71SBrad Bishop             return false;
679e45d8c71SBrad Bishop         }
680e45d8c71SBrad Bishop     }
681e45d8c71SBrad Bishop 
682e45d8c71SBrad Bishop     // validate checksum
683e45d8c71SBrad Bishop     size_t sum = 0;
684e45d8c71SBrad Bishop     for (int jj = 0; jj < 7; jj++)
685e45d8c71SBrad Bishop     {
686e45d8c71SBrad Bishop         sum += blockData[jj];
687e45d8c71SBrad Bishop     }
688e45d8c71SBrad Bishop     sum = (256 - sum) & 0xFF;
689e45d8c71SBrad Bishop 
690e45d8c71SBrad Bishop     if (sum != blockData[7])
691e45d8c71SBrad Bishop     {
692c3db2c3cSAlexander Hansen         lg2::debug(
693c3db2c3cSAlexander Hansen             "Checksum {CHECKSUM} is invalid. calculated checksum is {CALCULATED_CHECKSUM}",
694c3db2c3cSAlexander Hansen             "CHECKSUM", lg2::hex, blockData[7], "CALCULATED_CHECKSUM", lg2::hex,
695c3db2c3cSAlexander Hansen             sum);
696e45d8c71SBrad Bishop         return false;
697e45d8c71SBrad Bishop     }
698e45d8c71SBrad Bishop     return true;
699e45d8c71SBrad Bishop }
700e45d8c71SBrad Bishop 
findFRUHeader(FRUReader & reader,const std::string & errorHelp,std::array<uint8_t,I2C_SMBUS_BLOCK_MAX> & blockData,off_t & baseOffset)701309c0b13SZev Weiss bool findFRUHeader(FRUReader& reader, const std::string& errorHelp,
702e45d8c71SBrad Bishop                    std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData,
703e45d8c71SBrad Bishop                    off_t& baseOffset)
704e45d8c71SBrad Bishop {
705309c0b13SZev Weiss     if (reader.read(baseOffset, 0x8, blockData.data()) < 0)
706e45d8c71SBrad Bishop     {
707e45d8c71SBrad Bishop         std::cerr << "failed to read " << errorHelp << " base offset "
708e45d8c71SBrad Bishop                   << baseOffset << "\n";
709e45d8c71SBrad Bishop         return false;
710e45d8c71SBrad Bishop     }
711e45d8c71SBrad Bishop 
712e45d8c71SBrad Bishop     // check the header checksum
713e45d8c71SBrad Bishop     if (validateHeader(blockData))
714e45d8c71SBrad Bishop     {
715e45d8c71SBrad Bishop         return true;
716e45d8c71SBrad Bishop     }
717e45d8c71SBrad Bishop 
718e45d8c71SBrad Bishop     // only continue the search if we just looked at 0x0.
719e45d8c71SBrad Bishop     if (baseOffset != 0)
720e45d8c71SBrad Bishop     {
721e45d8c71SBrad Bishop         return false;
722e45d8c71SBrad Bishop     }
723e45d8c71SBrad Bishop 
724e45d8c71SBrad Bishop     // now check for special cases where the IPMI data is at an offset
725e45d8c71SBrad Bishop 
726e45d8c71SBrad Bishop     // check if blockData starts with tyanHeader
727e45d8c71SBrad Bishop     const std::vector<uint8_t> tyanHeader = {'$', 'T', 'Y', 'A', 'N', '$'};
728e45d8c71SBrad Bishop     if (blockData.size() >= tyanHeader.size() &&
729e45d8c71SBrad Bishop         std::equal(tyanHeader.begin(), tyanHeader.end(), blockData.begin()))
730e45d8c71SBrad Bishop     {
731e45d8c71SBrad Bishop         // look for the FRU header at offset 0x6000
732e45d8c71SBrad Bishop         baseOffset = 0x6000;
733309c0b13SZev Weiss         return findFRUHeader(reader, errorHelp, blockData, baseOffset);
734e45d8c71SBrad Bishop     }
735e45d8c71SBrad Bishop 
736c3db2c3cSAlexander Hansen     lg2::debug("Illegal header {HEADER} base offset {OFFSET}", "HEADER",
737c3db2c3cSAlexander Hansen                errorHelp, "OFFSET", baseOffset);
738e45d8c71SBrad Bishop 
739e45d8c71SBrad Bishop     return false;
740e45d8c71SBrad Bishop }
741e45d8c71SBrad Bishop 
readFRUContents(FRUReader & reader,const std::string & errorHelp)742*5a80703cSPatrick Williams std::pair<std::vector<uint8_t>, bool> readFRUContents(
743*5a80703cSPatrick Williams     FRUReader& reader, const std::string& errorHelp)
744e45d8c71SBrad Bishop {
7453013fb49SEd Tanous     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
746e45d8c71SBrad Bishop     off_t baseOffset = 0x0;
747e45d8c71SBrad Bishop 
748309c0b13SZev Weiss     if (!findFRUHeader(reader, errorHelp, blockData, baseOffset))
749e45d8c71SBrad Bishop     {
7502b3ed30cSMarvin Drees         return {{}, false};
751e45d8c71SBrad Bishop     }
752e45d8c71SBrad Bishop 
753e45d8c71SBrad Bishop     std::vector<uint8_t> device;
754e45d8c71SBrad Bishop     device.insert(device.end(), blockData.begin(), blockData.begin() + 8);
755e45d8c71SBrad Bishop 
756e45d8c71SBrad Bishop     bool hasMultiRecords = false;
757e45d8c71SBrad Bishop     size_t fruLength = fruBlockSize; // At least FRU header is present
758e45d8c71SBrad Bishop     unsigned int prevOffset = 0;
759e45d8c71SBrad Bishop     for (fruAreas area = fruAreas::fruAreaInternal;
760e45d8c71SBrad Bishop          area <= fruAreas::fruAreaMultirecord; ++area)
761e45d8c71SBrad Bishop     {
762e45d8c71SBrad Bishop         // Offset value can be 255.
763e45d8c71SBrad Bishop         unsigned int areaOffset = device[getHeaderAreaFieldOffset(area)];
764e45d8c71SBrad Bishop         if (areaOffset == 0)
765e45d8c71SBrad Bishop         {
766e45d8c71SBrad Bishop             continue;
767e45d8c71SBrad Bishop         }
768e45d8c71SBrad Bishop 
769e45d8c71SBrad Bishop         /* Check for offset order, as per Section 17 of FRU specification, FRU
770e45d8c71SBrad Bishop          * information areas are required to be in order in FRU data layout
771e45d8c71SBrad Bishop          * which means all offset value should be in increasing order or can be
772e45d8c71SBrad Bishop          * 0 if that area is not present
773e45d8c71SBrad Bishop          */
774e45d8c71SBrad Bishop         if (areaOffset <= prevOffset)
775e45d8c71SBrad Bishop         {
776e45d8c71SBrad Bishop             std::cerr << "Fru area offsets are not in required order as per "
777e45d8c71SBrad Bishop                          "Section 17 of Fru specification\n";
7782b3ed30cSMarvin Drees             return {{}, true};
779e45d8c71SBrad Bishop         }
780e45d8c71SBrad Bishop         prevOffset = areaOffset;
781e45d8c71SBrad Bishop 
782e45d8c71SBrad Bishop         // MultiRecords are different. area is not tracking section, it's
783e45d8c71SBrad Bishop         // walking the common header.
784e45d8c71SBrad Bishop         if (area == fruAreas::fruAreaMultirecord)
785e45d8c71SBrad Bishop         {
786e45d8c71SBrad Bishop             hasMultiRecords = true;
787e45d8c71SBrad Bishop             break;
788e45d8c71SBrad Bishop         }
789e45d8c71SBrad Bishop 
790e45d8c71SBrad Bishop         areaOffset *= fruBlockSize;
791e45d8c71SBrad Bishop 
792309c0b13SZev Weiss         if (reader.read(baseOffset + areaOffset, 0x2, blockData.data()) < 0)
793e45d8c71SBrad Bishop         {
794e45d8c71SBrad Bishop             std::cerr << "failed to read " << errorHelp << " base offset "
795e45d8c71SBrad Bishop                       << baseOffset << "\n";
7962b3ed30cSMarvin Drees             return {{}, true};
797e45d8c71SBrad Bishop         }
798e45d8c71SBrad Bishop 
799e45d8c71SBrad Bishop         // Ignore data type (blockData is already unsigned).
800e45d8c71SBrad Bishop         size_t length = blockData[1] * fruBlockSize;
801e45d8c71SBrad Bishop         areaOffset += length;
802e45d8c71SBrad Bishop         fruLength = (areaOffset > fruLength) ? areaOffset : fruLength;
803e45d8c71SBrad Bishop     }
804e45d8c71SBrad Bishop 
805e45d8c71SBrad Bishop     if (hasMultiRecords)
806e45d8c71SBrad Bishop     {
807e45d8c71SBrad Bishop         // device[area count] is the index to the last area because the 0th
808e45d8c71SBrad Bishop         // entry is not an offset in the common header.
809e45d8c71SBrad Bishop         unsigned int areaOffset =
810e45d8c71SBrad Bishop             device[getHeaderAreaFieldOffset(fruAreas::fruAreaMultirecord)];
811e45d8c71SBrad Bishop         areaOffset *= fruBlockSize;
812e45d8c71SBrad Bishop 
813e45d8c71SBrad Bishop         // the multi-area record header is 5 bytes long.
814e45d8c71SBrad Bishop         constexpr size_t multiRecordHeaderSize = 5;
815e45d8c71SBrad Bishop         constexpr uint8_t multiRecordEndOfListMask = 0x80;
816e45d8c71SBrad Bishop 
817e45d8c71SBrad Bishop         // Sanity hard-limit to 64KB.
818e45d8c71SBrad Bishop         while (areaOffset < std::numeric_limits<uint16_t>::max())
819e45d8c71SBrad Bishop         {
820e45d8c71SBrad Bishop             // In multi-area, the area offset points to the 0th record, each
821e45d8c71SBrad Bishop             // record has 3 bytes of the header we care about.
822309c0b13SZev Weiss             if (reader.read(baseOffset + areaOffset, 0x3, blockData.data()) < 0)
823e45d8c71SBrad Bishop             {
824e45d8c71SBrad Bishop                 std::cerr << "failed to read " << errorHelp << " base offset "
825e45d8c71SBrad Bishop                           << baseOffset << "\n";
8262b3ed30cSMarvin Drees                 return {{}, true};
827e45d8c71SBrad Bishop             }
828e45d8c71SBrad Bishop 
829e45d8c71SBrad Bishop             // Ok, let's check the record length, which is in bytes (unsigned,
830e45d8c71SBrad Bishop             // up to 255, so blockData should hold uint8_t not char)
831e45d8c71SBrad Bishop             size_t recordLength = blockData[2];
832e45d8c71SBrad Bishop             areaOffset += (recordLength + multiRecordHeaderSize);
833e45d8c71SBrad Bishop             fruLength = (areaOffset > fruLength) ? areaOffset : fruLength;
834e45d8c71SBrad Bishop 
835e45d8c71SBrad Bishop             // If this is the end of the list bail.
8363013fb49SEd Tanous             if ((blockData[1] & multiRecordEndOfListMask) != 0)
837e45d8c71SBrad Bishop             {
838e45d8c71SBrad Bishop                 break;
839e45d8c71SBrad Bishop             }
840e45d8c71SBrad Bishop         }
841e45d8c71SBrad Bishop     }
842e45d8c71SBrad Bishop 
843e45d8c71SBrad Bishop     // You already copied these first 8 bytes (the ipmi fru header size)
844e45d8c71SBrad Bishop     fruLength -= std::min(fruBlockSize, fruLength);
845e45d8c71SBrad Bishop 
846e45d8c71SBrad Bishop     int readOffset = fruBlockSize;
847e45d8c71SBrad Bishop 
848e45d8c71SBrad Bishop     while (fruLength > 0)
849e45d8c71SBrad Bishop     {
850e45d8c71SBrad Bishop         size_t requestLength =
851e45d8c71SBrad Bishop             std::min(static_cast<size_t>(I2C_SMBUS_BLOCK_MAX), fruLength);
852e45d8c71SBrad Bishop 
853309c0b13SZev Weiss         if (reader.read(baseOffset + readOffset, requestLength,
854309c0b13SZev Weiss                         blockData.data()) < 0)
855e45d8c71SBrad Bishop         {
856e45d8c71SBrad Bishop             std::cerr << "failed to read " << errorHelp << " base offset "
857e45d8c71SBrad Bishop                       << baseOffset << "\n";
8582b3ed30cSMarvin Drees             return {{}, true};
859e45d8c71SBrad Bishop         }
860e45d8c71SBrad Bishop 
861e45d8c71SBrad Bishop         device.insert(device.end(), blockData.begin(),
862e45d8c71SBrad Bishop                       blockData.begin() + requestLength);
863e45d8c71SBrad Bishop 
864e45d8c71SBrad Bishop         readOffset += requestLength;
865e45d8c71SBrad Bishop         fruLength -= std::min(requestLength, fruLength);
866e45d8c71SBrad Bishop     }
867e45d8c71SBrad Bishop 
8682b3ed30cSMarvin Drees     return {device, true};
869e45d8c71SBrad Bishop }
870e45d8c71SBrad Bishop 
getHeaderAreaFieldOffset(fruAreas area)871e45d8c71SBrad Bishop unsigned int getHeaderAreaFieldOffset(fruAreas area)
872e45d8c71SBrad Bishop {
873e45d8c71SBrad Bishop     return static_cast<unsigned int>(area) + 1;
874e45d8c71SBrad Bishop }
875e45d8c71SBrad Bishop 
getFRUInfo(const uint16_t & bus,const uint8_t & address)876213ee21fSkrishnar4 std::vector<uint8_t>& getFRUInfo(const uint16_t& bus, const uint8_t& address)
877e45d8c71SBrad Bishop {
878e45d8c71SBrad Bishop     auto deviceMap = busMap.find(bus);
879e45d8c71SBrad Bishop     if (deviceMap == busMap.end())
880e45d8c71SBrad Bishop     {
881e45d8c71SBrad Bishop         throw std::invalid_argument("Invalid Bus.");
882e45d8c71SBrad Bishop     }
883e45d8c71SBrad Bishop     auto device = deviceMap->second->find(address);
884e45d8c71SBrad Bishop     if (device == deviceMap->second->end())
885e45d8c71SBrad Bishop     {
886e45d8c71SBrad Bishop         throw std::invalid_argument("Invalid Address.");
887e45d8c71SBrad Bishop     }
888e45d8c71SBrad Bishop     std::vector<uint8_t>& ret = device->second;
889e45d8c71SBrad Bishop 
890e45d8c71SBrad Bishop     return ret;
891e45d8c71SBrad Bishop }
892bdfc5ec6SKumar Thangavel 
893bdfc5ec6SKumar Thangavel // Iterate FruArea Names and find start and size of the fru area that contains
894bdfc5ec6SKumar Thangavel // the propertyName and the field start location for the property. fruAreaParams
895bdfc5ec6SKumar Thangavel // struct values fruAreaStart, fruAreaSize, fruAreaEnd, fieldLoc values gets
896bdfc5ec6SKumar Thangavel // updated/returned if successful.
897bdfc5ec6SKumar Thangavel 
findFruAreaLocationAndField(std::vector<uint8_t> & fruData,const std::string & propertyName,struct FruArea & fruAreaParams)898bdfc5ec6SKumar Thangavel bool findFruAreaLocationAndField(std::vector<uint8_t>& fruData,
899bdfc5ec6SKumar Thangavel                                  const std::string& propertyName,
90051b557b7SKumar Thangavel                                  struct FruArea& fruAreaParams)
901bdfc5ec6SKumar Thangavel {
902bdfc5ec6SKumar Thangavel     const std::vector<std::string>* fruAreaFieldNames = nullptr;
903bdfc5ec6SKumar Thangavel 
904bdfc5ec6SKumar Thangavel     uint8_t fruAreaOffsetFieldValue = 0;
905bdfc5ec6SKumar Thangavel     size_t offset = 0;
906bdfc5ec6SKumar Thangavel     std::string areaName = propertyName.substr(0, propertyName.find('_'));
907bdfc5ec6SKumar Thangavel     std::string propertyNamePrefix = areaName + "_";
908bdfc5ec6SKumar Thangavel     auto it = std::find(fruAreaNames.begin(), fruAreaNames.end(), areaName);
909bdfc5ec6SKumar Thangavel     if (it == fruAreaNames.end())
910bdfc5ec6SKumar Thangavel     {
911bdfc5ec6SKumar Thangavel         std::cerr << "Can't parse area name for property " << propertyName
912bdfc5ec6SKumar Thangavel                   << " \n";
913bdfc5ec6SKumar Thangavel         return false;
914bdfc5ec6SKumar Thangavel     }
915bdfc5ec6SKumar Thangavel     fruAreas fruAreaToUpdate = static_cast<fruAreas>(it - fruAreaNames.begin());
916bdfc5ec6SKumar Thangavel     fruAreaOffsetFieldValue =
917bdfc5ec6SKumar Thangavel         fruData[getHeaderAreaFieldOffset(fruAreaToUpdate)];
918bdfc5ec6SKumar Thangavel     switch (fruAreaToUpdate)
919bdfc5ec6SKumar Thangavel     {
920bdfc5ec6SKumar Thangavel         case fruAreas::fruAreaChassis:
921bdfc5ec6SKumar Thangavel             offset = 3; // chassis part number offset. Skip fixed first 3 bytes
922bdfc5ec6SKumar Thangavel             fruAreaFieldNames = &chassisFruAreas;
923bdfc5ec6SKumar Thangavel             break;
924bdfc5ec6SKumar Thangavel         case fruAreas::fruAreaBoard:
925bdfc5ec6SKumar Thangavel             offset = 6; // board manufacturer offset. Skip fixed first 6 bytes
926bdfc5ec6SKumar Thangavel             fruAreaFieldNames = &boardFruAreas;
927bdfc5ec6SKumar Thangavel             break;
928bdfc5ec6SKumar Thangavel         case fruAreas::fruAreaProduct:
929bdfc5ec6SKumar Thangavel             // Manufacturer name offset. Skip fixed first 3 product fru bytes
930bdfc5ec6SKumar Thangavel             // i.e. version, area length and language code
931bdfc5ec6SKumar Thangavel             offset = 3;
932bdfc5ec6SKumar Thangavel             fruAreaFieldNames = &productFruAreas;
933bdfc5ec6SKumar Thangavel             break;
934bdfc5ec6SKumar Thangavel         default:
935bdfc5ec6SKumar Thangavel             std::cerr << "Invalid PropertyName " << propertyName << " \n";
936bdfc5ec6SKumar Thangavel             return false;
937bdfc5ec6SKumar Thangavel     }
938bdfc5ec6SKumar Thangavel     if (fruAreaOffsetFieldValue == 0)
939bdfc5ec6SKumar Thangavel     {
940bdfc5ec6SKumar Thangavel         std::cerr << "FRU Area for " << propertyName << " not present \n";
941bdfc5ec6SKumar Thangavel         return false;
942bdfc5ec6SKumar Thangavel     }
943bdfc5ec6SKumar Thangavel 
944bdfc5ec6SKumar Thangavel     fruAreaParams.start = fruAreaOffsetFieldValue * fruBlockSize;
945bdfc5ec6SKumar Thangavel     fruAreaParams.size = fruData[fruAreaParams.start + 1] * fruBlockSize;
946bdfc5ec6SKumar Thangavel     fruAreaParams.end = fruAreaParams.start + fruAreaParams.size;
94751b557b7SKumar Thangavel     size_t fruDataIter = fruAreaParams.start + offset;
948bdfc5ec6SKumar Thangavel     size_t skipToFRUUpdateField = 0;
949bdfc5ec6SKumar Thangavel     ssize_t fieldLength = 0;
950bdfc5ec6SKumar Thangavel 
951bdfc5ec6SKumar Thangavel     bool found = false;
952bdfc5ec6SKumar Thangavel     for (const auto& field : *fruAreaFieldNames)
953bdfc5ec6SKumar Thangavel     {
954bdfc5ec6SKumar Thangavel         skipToFRUUpdateField++;
955bdfc5ec6SKumar Thangavel         if (propertyName == propertyNamePrefix + field)
956bdfc5ec6SKumar Thangavel         {
957bdfc5ec6SKumar Thangavel             found = true;
958bdfc5ec6SKumar Thangavel             break;
959bdfc5ec6SKumar Thangavel         }
960bdfc5ec6SKumar Thangavel     }
961bdfc5ec6SKumar Thangavel     if (!found)
962bdfc5ec6SKumar Thangavel     {
963bdfc5ec6SKumar Thangavel         std::size_t pos = propertyName.find(fruCustomFieldName);
964bdfc5ec6SKumar Thangavel         if (pos == std::string::npos)
965bdfc5ec6SKumar Thangavel         {
966bdfc5ec6SKumar Thangavel             std::cerr << "PropertyName doesn't exist in FRU Area Vectors: "
967bdfc5ec6SKumar Thangavel                       << propertyName << "\n";
968bdfc5ec6SKumar Thangavel             return false;
969bdfc5ec6SKumar Thangavel         }
970bdfc5ec6SKumar Thangavel         std::string fieldNumStr =
971bdfc5ec6SKumar Thangavel             propertyName.substr(pos + fruCustomFieldName.length());
972bdfc5ec6SKumar Thangavel         size_t fieldNum = std::stoi(fieldNumStr);
973bdfc5ec6SKumar Thangavel         if (fieldNum == 0)
974bdfc5ec6SKumar Thangavel         {
975bdfc5ec6SKumar Thangavel             std::cerr << "PropertyName not recognized: " << propertyName
976bdfc5ec6SKumar Thangavel                       << "\n";
977bdfc5ec6SKumar Thangavel             return false;
978bdfc5ec6SKumar Thangavel         }
979bdfc5ec6SKumar Thangavel         skipToFRUUpdateField += fieldNum;
980bdfc5ec6SKumar Thangavel     }
981bdfc5ec6SKumar Thangavel 
982bdfc5ec6SKumar Thangavel     for (size_t i = 1; i < skipToFRUUpdateField; i++)
983bdfc5ec6SKumar Thangavel     {
984bdfc5ec6SKumar Thangavel         if (fruDataIter < fruData.size())
985bdfc5ec6SKumar Thangavel         {
986bdfc5ec6SKumar Thangavel             fieldLength = getFieldLength(fruData[fruDataIter]);
987bdfc5ec6SKumar Thangavel 
988bdfc5ec6SKumar Thangavel             if (fieldLength < 0)
989bdfc5ec6SKumar Thangavel             {
990bdfc5ec6SKumar Thangavel                 break;
991bdfc5ec6SKumar Thangavel             }
992bdfc5ec6SKumar Thangavel             fruDataIter += 1 + fieldLength;
993bdfc5ec6SKumar Thangavel         }
994bdfc5ec6SKumar Thangavel     }
995bdfc5ec6SKumar Thangavel     fruAreaParams.updateFieldLoc = fruDataIter;
996bdfc5ec6SKumar Thangavel 
997bdfc5ec6SKumar Thangavel     return true;
998bdfc5ec6SKumar Thangavel }
99951b557b7SKumar Thangavel 
100051b557b7SKumar Thangavel // Copy the FRU Area fields and properties into restFRUAreaFieldsData vector.
100151b557b7SKumar Thangavel // Return true for success and false for failure.
100251b557b7SKumar Thangavel 
copyRestFRUArea(std::vector<uint8_t> & fruData,const std::string & propertyName,struct FruArea & fruAreaParams,std::vector<uint8_t> & restFRUAreaFieldsData)100351b557b7SKumar Thangavel bool copyRestFRUArea(std::vector<uint8_t>& fruData,
100451b557b7SKumar Thangavel                      const std::string& propertyName,
100551b557b7SKumar Thangavel                      struct FruArea& fruAreaParams,
100651b557b7SKumar Thangavel                      std::vector<uint8_t>& restFRUAreaFieldsData)
100751b557b7SKumar Thangavel {
100851b557b7SKumar Thangavel     size_t fieldLoc = fruAreaParams.updateFieldLoc;
100951b557b7SKumar Thangavel     size_t start = fruAreaParams.start;
101051b557b7SKumar Thangavel     size_t fruAreaSize = fruAreaParams.size;
101151b557b7SKumar Thangavel 
101251b557b7SKumar Thangavel     // Push post update fru field bytes to a vector
101351b557b7SKumar Thangavel     ssize_t fieldLength = getFieldLength(fruData[fieldLoc]);
101451b557b7SKumar Thangavel     if (fieldLength < 0)
101551b557b7SKumar Thangavel     {
101651b557b7SKumar Thangavel         std::cerr << "Property " << propertyName << " not present \n";
101751b557b7SKumar Thangavel         return false;
101851b557b7SKumar Thangavel     }
101951b557b7SKumar Thangavel 
102051b557b7SKumar Thangavel     size_t fruDataIter = 0;
102151b557b7SKumar Thangavel     fruDataIter = fieldLoc;
102251b557b7SKumar Thangavel     fruDataIter += 1 + fieldLength;
102351b557b7SKumar Thangavel     size_t restFRUFieldsLoc = fruDataIter;
102451b557b7SKumar Thangavel     size_t endOfFieldsLoc = 0;
102551b557b7SKumar Thangavel 
102651b557b7SKumar Thangavel     if (fruDataIter < fruData.size())
102751b557b7SKumar Thangavel     {
102851b557b7SKumar Thangavel         while ((fieldLength = getFieldLength(fruData[fruDataIter])) >= 0)
102951b557b7SKumar Thangavel         {
103051b557b7SKumar Thangavel             if (fruDataIter >= (start + fruAreaSize))
103151b557b7SKumar Thangavel             {
103251b557b7SKumar Thangavel                 fruDataIter = start + fruAreaSize;
103351b557b7SKumar Thangavel                 break;
103451b557b7SKumar Thangavel             }
103551b557b7SKumar Thangavel             fruDataIter += 1 + fieldLength;
103651b557b7SKumar Thangavel         }
103751b557b7SKumar Thangavel         endOfFieldsLoc = fruDataIter;
103851b557b7SKumar Thangavel     }
103951b557b7SKumar Thangavel 
104051b557b7SKumar Thangavel     std::copy_n(fruData.begin() + restFRUFieldsLoc,
104151b557b7SKumar Thangavel                 endOfFieldsLoc - restFRUFieldsLoc + 1,
104251b557b7SKumar Thangavel                 std::back_inserter(restFRUAreaFieldsData));
104351b557b7SKumar Thangavel 
104451b557b7SKumar Thangavel     fruAreaParams.restFieldsLoc = restFRUFieldsLoc;
104551b557b7SKumar Thangavel     fruAreaParams.restFieldsEnd = endOfFieldsLoc;
104651b557b7SKumar Thangavel 
104751b557b7SKumar Thangavel     return true;
104851b557b7SKumar Thangavel }
1049d79d0251SKumar Thangavel 
1050d79d0251SKumar Thangavel // Get all device dbus path and match path with product name using
1051d79d0251SKumar Thangavel // regular expression and find the device index for all devices.
1052d79d0251SKumar Thangavel 
findIndexForFRU(boost::container::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,std::string & productName)1053d79d0251SKumar Thangavel std::optional<int> findIndexForFRU(
1054d79d0251SKumar Thangavel     boost::container::flat_map<
1055d79d0251SKumar Thangavel         std::pair<size_t, size_t>,
1056d79d0251SKumar Thangavel         std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
1057d79d0251SKumar Thangavel     std::string& productName)
1058d79d0251SKumar Thangavel {
1059d79d0251SKumar Thangavel     int highest = -1;
1060d79d0251SKumar Thangavel     bool found = false;
1061d79d0251SKumar Thangavel 
1062df190619SPatrick Williams     for (const auto& busIface : dbusInterfaceMap)
1063d79d0251SKumar Thangavel     {
1064d79d0251SKumar Thangavel         std::string path = busIface.second->get_object_path();
1065d79d0251SKumar Thangavel         if (std::regex_match(path, std::regex(productName + "(_\\d+|)$")))
1066d79d0251SKumar Thangavel         {
1067d79d0251SKumar Thangavel             // Check if the match named has extra information.
1068d79d0251SKumar Thangavel             found = true;
1069d79d0251SKumar Thangavel             std::smatch baseMatch;
1070d79d0251SKumar Thangavel 
1071d79d0251SKumar Thangavel             bool match = std::regex_match(path, baseMatch,
1072d79d0251SKumar Thangavel                                           std::regex(productName + "_(\\d+)$"));
1073d79d0251SKumar Thangavel             if (match)
1074d79d0251SKumar Thangavel             {
1075d79d0251SKumar Thangavel                 if (baseMatch.size() == 2)
1076d79d0251SKumar Thangavel                 {
1077d79d0251SKumar Thangavel                     std::ssub_match baseSubMatch = baseMatch[1];
1078d79d0251SKumar Thangavel                     std::string base = baseSubMatch.str();
1079d79d0251SKumar Thangavel 
1080d79d0251SKumar Thangavel                     int value = std::stoi(base);
1081d79d0251SKumar Thangavel                     highest = (value > highest) ? value : highest;
1082d79d0251SKumar Thangavel                 }
1083d79d0251SKumar Thangavel             }
1084d79d0251SKumar Thangavel         }
1085d79d0251SKumar Thangavel     } // end searching objects
1086d79d0251SKumar Thangavel 
1087d79d0251SKumar Thangavel     if (!found)
1088d79d0251SKumar Thangavel     {
1089d79d0251SKumar Thangavel         return std::nullopt;
1090d79d0251SKumar Thangavel     }
1091d79d0251SKumar Thangavel     return highest;
1092d79d0251SKumar Thangavel }
10939f2162afSKumar Thangavel 
10949f2162afSKumar Thangavel // This function does format fru data as per IPMI format and find the
10959f2162afSKumar Thangavel // productName in the formatted fru data, get that productName and return
10969f2162afSKumar Thangavel // productName if found or return NULL.
10979f2162afSKumar Thangavel 
getProductName(std::vector<uint8_t> & device,boost::container::flat_map<std::string,std::string> & formattedFRU,uint32_t bus,uint32_t address,size_t & unknownBusObjectCount)10989f2162afSKumar Thangavel std::optional<std::string> getProductName(
10999f2162afSKumar Thangavel     std::vector<uint8_t>& device,
11009f2162afSKumar Thangavel     boost::container::flat_map<std::string, std::string>& formattedFRU,
11019f2162afSKumar Thangavel     uint32_t bus, uint32_t address, size_t& unknownBusObjectCount)
11029f2162afSKumar Thangavel {
11039f2162afSKumar Thangavel     std::string productName;
11049f2162afSKumar Thangavel 
11059f2162afSKumar Thangavel     resCodes res = formatIPMIFRU(device, formattedFRU);
11069f2162afSKumar Thangavel     if (res == resCodes::resErr)
11079f2162afSKumar Thangavel     {
11089f2162afSKumar Thangavel         std::cerr << "failed to parse FRU for device at bus " << bus
11099f2162afSKumar Thangavel                   << " address " << address << "\n";
11109f2162afSKumar Thangavel         return std::nullopt;
11119f2162afSKumar Thangavel     }
11129f2162afSKumar Thangavel     if (res == resCodes::resWarn)
11139f2162afSKumar Thangavel     {
11149f2162afSKumar Thangavel         std::cerr << "Warnings while parsing FRU for device at bus " << bus
11159f2162afSKumar Thangavel                   << " address " << address << "\n";
11169f2162afSKumar Thangavel     }
11179f2162afSKumar Thangavel 
11189f2162afSKumar Thangavel     auto productNameFind = formattedFRU.find("BOARD_PRODUCT_NAME");
11199f2162afSKumar Thangavel     // Not found under Board section or an empty string.
11209f2162afSKumar Thangavel     if (productNameFind == formattedFRU.end() ||
11219f2162afSKumar Thangavel         productNameFind->second.empty())
11229f2162afSKumar Thangavel     {
11239f2162afSKumar Thangavel         productNameFind = formattedFRU.find("PRODUCT_PRODUCT_NAME");
11249f2162afSKumar Thangavel     }
11259f2162afSKumar Thangavel     // Found under Product section and not an empty string.
11269f2162afSKumar Thangavel     if (productNameFind != formattedFRU.end() &&
11279f2162afSKumar Thangavel         !productNameFind->second.empty())
11289f2162afSKumar Thangavel     {
11299f2162afSKumar Thangavel         productName = productNameFind->second;
11309f2162afSKumar Thangavel         std::regex illegalObject("[^A-Za-z0-9_]");
11319f2162afSKumar Thangavel         productName = std::regex_replace(productName, illegalObject, "_");
11329f2162afSKumar Thangavel     }
11339f2162afSKumar Thangavel     else
11349f2162afSKumar Thangavel     {
11359f2162afSKumar Thangavel         productName = "UNKNOWN" + std::to_string(unknownBusObjectCount);
11369f2162afSKumar Thangavel         unknownBusObjectCount++;
11379f2162afSKumar Thangavel     }
11389f2162afSKumar Thangavel     return productName;
11399f2162afSKumar Thangavel }
11409d6f5904SKumar Thangavel 
getFruData(std::vector<uint8_t> & fruData,uint32_t bus,uint32_t address)11419d6f5904SKumar Thangavel bool getFruData(std::vector<uint8_t>& fruData, uint32_t bus, uint32_t address)
11429d6f5904SKumar Thangavel {
11439d6f5904SKumar Thangavel     try
11449d6f5904SKumar Thangavel     {
11459d6f5904SKumar Thangavel         fruData = getFRUInfo(static_cast<uint16_t>(bus),
11469d6f5904SKumar Thangavel                              static_cast<uint8_t>(address));
11479d6f5904SKumar Thangavel     }
11489d6f5904SKumar Thangavel     catch (const std::invalid_argument& e)
11499d6f5904SKumar Thangavel     {
11509d6f5904SKumar Thangavel         std::cerr << "Failure getting FRU Info" << e.what() << "\n";
11519d6f5904SKumar Thangavel         return false;
11529d6f5904SKumar Thangavel     }
11539d6f5904SKumar Thangavel 
11549d6f5904SKumar Thangavel     return !fruData.empty();
11559d6f5904SKumar Thangavel }
1156