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