blob: a3f892157bbb53d4ceb4d1f4d4611eb614e68b20 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#pragma once
2
3#include "config.h"
4
5#include "constants.hpp"
Rekha Aparnaa5be7222025-09-01 00:42:22 -05006#include "error_codes.hpp"
Souvik Roy815c6022025-02-20 00:54:24 -06007#include "event_logger.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05008#include "exceptions.hpp"
9#include "logger.hpp"
10#include "types.hpp"
11
12#include <nlohmann/json.hpp>
13#include <utility/common_utility.hpp>
14#include <utility/dbus_utility.hpp>
15
16#include <filesystem>
17#include <fstream>
18#include <regex>
Souvik Roy815c6022025-02-20 00:54:24 -060019#include <typeindex>
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050020
21namespace vpd
22{
23namespace vpdSpecificUtility
24{
25/**
26 * @brief API to generate file name for bad VPD.
27 *
28 * For i2c eeproms - the pattern of the vpd-name will be
29 * i2c-<bus-number>-<eeprom-address>.
30 * For spi eeproms - the pattern of the vpd-name will be spi-<spi-number>.
31 *
Souvik Roy8fc12522025-02-19 01:28:38 -060032 * @param[in] i_vpdFilePath - file path of the vpd.
33 *
34 * @return On success, returns generated file name, otherwise returns empty
35 * string.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050036 */
Souvik Roy8fc12522025-02-19 01:28:38 -060037inline std::string generateBadVPDFileName(
38 const std::string& i_vpdFilePath) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050039{
Souvik Roy0f370432025-04-08 01:55:58 -050040 std::string l_badVpdFileName{constants::badVpdDir};
Souvik Roy8fc12522025-02-19 01:28:38 -060041 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050042 {
Souvik Roy8fc12522025-02-19 01:28:38 -060043 if (i_vpdFilePath.find("i2c") != std::string::npos)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050044 {
Souvik Roy8fc12522025-02-19 01:28:38 -060045 l_badVpdFileName += "i2c-";
46 std::regex l_i2cPattern("(at24/)([0-9]+-[0-9]+)\\/");
47 std::smatch l_match;
48 if (std::regex_search(i_vpdFilePath, l_match, l_i2cPattern))
49 {
50 l_badVpdFileName += l_match.str(2);
51 }
52 }
53 else if (i_vpdFilePath.find("spi") != std::string::npos)
54 {
55 std::regex l_spiPattern("((spi)[0-9]+)(.0)");
56 std::smatch l_match;
57 if (std::regex_search(i_vpdFilePath, l_match, l_spiPattern))
58 {
59 l_badVpdFileName += l_match.str(1);
60 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050061 }
62 }
Souvik Roy8fc12522025-02-19 01:28:38 -060063 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050064 {
Souvik Roy8fc12522025-02-19 01:28:38 -060065 l_badVpdFileName.clear();
66 logging::logMessage("Failed to generate bad VPD file name for [" +
67 i_vpdFilePath + "]. Error: " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050068 }
Souvik Roy8fc12522025-02-19 01:28:38 -060069 return l_badVpdFileName;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050070}
71
72/**
73 * @brief API which dumps the broken/bad vpd in a directory.
74 * When the vpd is bad, this API places the bad vpd file inside
Souvik Roy0f370432025-04-08 01:55:58 -050075 * "/var/lib/vpd/dumps" in BMC, in order to collect bad VPD data as a part of
76 * user initiated BMC dump.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050077 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050078 *
Souvik Roy8fc12522025-02-19 01:28:38 -060079 * @param[in] i_vpdFilePath - vpd file path
80 * @param[in] i_vpdVector - vpd vector
81 *
82 * @return On success returns 0, otherwise returns -1.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050083 */
Souvik Roy8fc12522025-02-19 01:28:38 -060084inline int dumpBadVpd(const std::string& i_vpdFilePath,
85 const types::BinaryVector& i_vpdVector) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050086{
Souvik Roy8fc12522025-02-19 01:28:38 -060087 int l_rc{constants::FAILURE};
88 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050089 {
Souvik Roy0f370432025-04-08 01:55:58 -050090 std::filesystem::create_directory(constants::badVpdDir);
Souvik Roy8fc12522025-02-19 01:28:38 -060091 auto l_badVpdPath = generateBadVPDFileName(i_vpdFilePath);
92
93 if (l_badVpdPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050094 {
Souvik Roy8fc12522025-02-19 01:28:38 -060095 throw std::runtime_error("Failed to generate bad VPD file name");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050096 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050097
Souvik Roy8fc12522025-02-19 01:28:38 -060098 if (std::filesystem::exists(l_badVpdPath))
99 {
100 std::error_code l_ec;
101 std::filesystem::remove(l_badVpdPath, l_ec);
102 if (l_ec) // error code
103 {
104 const std::string l_errorMsg{
105 "Error removing the existing broken vpd in " +
106 l_badVpdPath +
107 ". Error code : " + std::to_string(l_ec.value()) +
108 ". Error message : " + l_ec.message()};
109
110 throw std::runtime_error(l_errorMsg);
111 }
112 }
113
114 std::ofstream l_badVpdFileStream(l_badVpdPath, std::ofstream::binary);
115 if (!l_badVpdFileStream.is_open())
116 {
Souvik Roy0f370432025-04-08 01:55:58 -0500117 const std::string l_errorMsg{
118 "Failed to open bad vpd file path [" + l_badVpdPath +
119 "]. Unable to dump the broken/bad vpd file."};
120
121 throw std::runtime_error(l_errorMsg);
Souvik Roy8fc12522025-02-19 01:28:38 -0600122 }
123
124 l_badVpdFileStream.write(
125 reinterpret_cast<const char*>(i_vpdVector.data()),
126 i_vpdVector.size());
127
128 l_rc = constants::SUCCESS;
129 }
130 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500131 {
Souvik Roy8fc12522025-02-19 01:28:38 -0600132 logging::logMessage("Failed to dump bad VPD for [" + i_vpdFilePath +
133 "]. Error: " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500134 }
Souvik Roy8fc12522025-02-19 01:28:38 -0600135 return l_rc;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500136}
137
138/**
139 * @brief An API to read value of a keyword.
140 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500141 *
Souvik Roya55fcca2025-02-19 01:33:58 -0600142 * @param[in] i_kwdValueMap - A map having Kwd value pair.
143 * @param[in] i_kwd - keyword name.
144 *
145 * @return On success returns value of the keyword read from map, otherwise
146 * returns empty string.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500147 */
Souvik Roya55fcca2025-02-19 01:33:58 -0600148inline std::string getKwVal(const types::IPZKwdValueMap& i_kwdValueMap,
149 const std::string& i_kwd) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500150{
Souvik Roya55fcca2025-02-19 01:33:58 -0600151 std::string l_kwdValue;
152 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500153 {
Souvik Roya55fcca2025-02-19 01:33:58 -0600154 if (i_kwd.empty())
155 {
156 throw std::runtime_error("Invalid parameters");
157 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500158
Souvik Roya55fcca2025-02-19 01:33:58 -0600159 auto l_itrToKwd = i_kwdValueMap.find(i_kwd);
160 if (l_itrToKwd != i_kwdValueMap.end())
161 {
162 l_kwdValue = l_itrToKwd->second;
163 }
164 else
165 {
166 throw std::runtime_error("Keyword not found");
167 }
168 }
169 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500170 {
Souvik Roya55fcca2025-02-19 01:33:58 -0600171 logging::logMessage("Failed to get value for keyword [" + i_kwd +
172 "]. Error : " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500173 }
Souvik Roya55fcca2025-02-19 01:33:58 -0600174 return l_kwdValue;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500175}
176
177/**
178 * @brief An API to process encoding of a keyword.
179 *
Souvik Royf277d6a2025-02-20 00:08:43 -0600180 * @param[in] i_keyword - Keyword to be processed.
181 * @param[in] i_encoding - Type of encoding.
182 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500183 * @return Value after being processed for encoded type.
184 */
Souvik Royf277d6a2025-02-20 00:08:43 -0600185inline std::string encodeKeyword(const std::string& i_keyword,
186 const std::string& i_encoding) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500187{
188 // Default value is keyword value
Souvik Royf277d6a2025-02-20 00:08:43 -0600189 std::string l_result(i_keyword.begin(), i_keyword.end());
190 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500191 {
Souvik Royf277d6a2025-02-20 00:08:43 -0600192 if (i_encoding == "MAC")
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500193 {
Souvik Royf277d6a2025-02-20 00:08:43 -0600194 l_result.clear();
195 size_t l_firstByte = i_keyword[0];
196 l_result += commonUtility::toHex(l_firstByte >> 4);
197 l_result += commonUtility::toHex(l_firstByte & 0x0f);
198 for (size_t i = 1; i < i_keyword.size(); ++i)
199 {
200 l_result += ":";
201 l_result += commonUtility::toHex(i_keyword[i] >> 4);
202 l_result += commonUtility::toHex(i_keyword[i] & 0x0f);
203 }
204 }
205 else if (i_encoding == "DATE")
206 {
207 // Date, represent as
208 // <year>-<month>-<day> <hour>:<min>
209 l_result.clear();
210 static constexpr uint8_t skipPrefix = 3;
211
212 auto strItr = i_keyword.begin();
213 advance(strItr, skipPrefix);
214 for_each(strItr, i_keyword.end(),
215 [&l_result](size_t c) { l_result += c; });
216
217 l_result.insert(constants::BD_YEAR_END, 1, '-');
218 l_result.insert(constants::BD_MONTH_END, 1, '-');
219 l_result.insert(constants::BD_DAY_END, 1, ' ');
220 l_result.insert(constants::BD_HOUR_END, 1, ':');
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500221 }
222 }
Souvik Royf277d6a2025-02-20 00:08:43 -0600223 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500224 {
Souvik Royf277d6a2025-02-20 00:08:43 -0600225 l_result.clear();
226 logging::logMessage("Failed to encode keyword [" + i_keyword +
227 "]. Error: " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500228 }
229
Souvik Royf277d6a2025-02-20 00:08:43 -0600230 return l_result;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500231}
232
233/**
234 * @brief Helper function to insert or merge in map.
235 *
236 * This method checks in an interface if the given interface exists. If the
237 * interface key already exists, property map is inserted corresponding to it.
238 * If the key does'nt exist then given interface and property map pair is newly
239 * created. If the property present in propertymap already exist in the
240 * InterfaceMap, then the new property value is ignored.
241 *
Souvik Royfa47e6c2025-02-20 00:17:25 -0600242 * @param[in,out] io_map - Interface map.
243 * @param[in] i_interface - Interface to be processed.
244 * @param[in] i_propertyMap - new property map that needs to be emplaced.
245 *
246 * @return On success returns 0, otherwise returns -1.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500247 */
Souvik Royfa47e6c2025-02-20 00:17:25 -0600248inline int insertOrMerge(types::InterfaceMap& io_map,
249 const std::string& i_interface,
250 types::PropertyMap&& i_propertyMap) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500251{
Souvik Royfa47e6c2025-02-20 00:17:25 -0600252 int l_rc{constants::FAILURE};
253 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500254 {
Souvik Royfa47e6c2025-02-20 00:17:25 -0600255 if (io_map.find(i_interface) != io_map.end())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500256 {
Souvik Royfa47e6c2025-02-20 00:17:25 -0600257 auto& l_prop = io_map.at(i_interface);
258 std::for_each(i_propertyMap.begin(), i_propertyMap.end(),
259 [&l_prop](auto l_keyValue) {
260 l_prop[l_keyValue.first] = l_keyValue.second;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500261 });
262 }
Souvik Royfa47e6c2025-02-20 00:17:25 -0600263 else
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500264 {
Souvik Royfa47e6c2025-02-20 00:17:25 -0600265 io_map.emplace(i_interface, i_propertyMap);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500266 }
Souvik Royfa47e6c2025-02-20 00:17:25 -0600267
268 l_rc = constants::SUCCESS;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500269 }
Souvik Royfa47e6c2025-02-20 00:17:25 -0600270 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500271 {
Souvik Royfa47e6c2025-02-20 00:17:25 -0600272 // ToDo:: Log PEL
273 logging::logMessage(
274 "Inserting properties into interface[" + i_interface +
275 "] map failed, reason: " + std::string(l_ex.what()));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500276 }
Souvik Royfa47e6c2025-02-20 00:17:25 -0600277 return l_rc;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500278}
279
280/**
281 * @brief API to expand unpanded location code.
282 *
283 * Note: The API handles all the exception internally, in case of any error
284 * unexpanded location code will be returned as it is.
285 *
286 * @param[in] unexpandedLocationCode - Unexpanded location code.
287 * @param[in] parsedVpdMap - Parsed VPD map.
288 * @return Expanded location code. In case of any error, unexpanded is returned
289 * as it is.
290 */
Patrick Williams43fedab2025-02-03 14:28:05 -0500291inline std::string getExpandedLocationCode(
292 const std::string& unexpandedLocationCode,
293 const types::VPDMapVariant& parsedVpdMap)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500294{
295 auto expanded{unexpandedLocationCode};
296
297 try
298 {
299 // Expanded location code is formed by combining two keywords
300 // depending on type in unexpanded one. Second one is always "SE".
301 std::string kwd1, kwd2{constants::kwdSE};
302
303 // interface to search for required keywords;
304 std::string kwdInterface;
305
306 // record which holds the required keywords.
307 std::string recordName;
308
309 auto pos = unexpandedLocationCode.find("fcs");
310 if (pos != std::string::npos)
311 {
312 kwd1 = constants::kwdFC;
313 kwdInterface = constants::vcenInf;
314 recordName = constants::recVCEN;
315 }
316 else
317 {
318 pos = unexpandedLocationCode.find("mts");
319 if (pos != std::string::npos)
320 {
321 kwd1 = constants::kwdTM;
322 kwdInterface = constants::vsysInf;
323 recordName = constants::recVSYS;
324 }
325 else
326 {
327 throw std::runtime_error(
328 "Error detecting type of unexpanded location code.");
329 }
330 }
331
332 std::string firstKwdValue, secondKwdValue;
333
334 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap);
335 ipzVpdMap && (*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
336 {
337 auto itrToVCEN = (*ipzVpdMap).find(recordName);
Souvik Roya55fcca2025-02-19 01:33:58 -0600338 firstKwdValue = getKwVal(itrToVCEN->second, kwd1);
339 if (firstKwdValue.empty())
340 {
341 throw std::runtime_error(
342 "Failed to get value for keyword [" + kwd1 + "]");
343 }
344
345 secondKwdValue = getKwVal(itrToVCEN->second, kwd2);
346 if (secondKwdValue.empty())
347 {
348 throw std::runtime_error(
349 "Failed to get value for keyword [" + kwd2 + "]");
350 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500351 }
352 else
353 {
354 std::array<const char*, 1> interfaceList = {kwdInterface.c_str()};
355
356 types::MapperGetObject mapperRetValue = dbusUtility::getObjectMap(
357 std::string(constants::systemVpdInvPath), interfaceList);
358
359 if (mapperRetValue.empty())
360 {
361 throw std::runtime_error("Mapper failed to get service");
362 }
363
364 const std::string& serviceName = std::get<0>(mapperRetValue.at(0));
365
366 auto retVal = dbusUtility::readDbusProperty(
367 serviceName, std::string(constants::systemVpdInvPath),
368 kwdInterface, kwd1);
369
370 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
371 {
372 firstKwdValue.assign(
373 reinterpret_cast<const char*>(kwdVal->data()),
374 kwdVal->size());
375 }
376 else
377 {
378 throw std::runtime_error(
379 "Failed to read value of " + kwd1 + " from Bus");
380 }
381
382 retVal = dbusUtility::readDbusProperty(
383 serviceName, std::string(constants::systemVpdInvPath),
384 kwdInterface, kwd2);
385
386 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
387 {
388 secondKwdValue.assign(
389 reinterpret_cast<const char*>(kwdVal->data()),
390 kwdVal->size());
391 }
392 else
393 {
394 throw std::runtime_error(
395 "Failed to read value of " + kwd2 + " from Bus");
396 }
397 }
398
399 if (unexpandedLocationCode.find("fcs") != std::string::npos)
400 {
401 // TODO: See if ND0 can be placed in the JSON
402 expanded.replace(
403 pos, 3, firstKwdValue.substr(0, 4) + ".ND0." + secondKwdValue);
404 }
405 else
406 {
407 replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
408 expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
409 }
410 }
411 catch (const std::exception& ex)
412 {
413 logging::logMessage("Failed to expand location code with exception: " +
414 std::string(ex.what()));
415 }
416
417 return expanded;
418}
419
420/**
421 * @brief An API to get VPD in a vector.
422 *
423 * The vector is required by the respective parser to fill the VPD map.
424 * Note: API throws exception in case of failure. Caller needs to handle.
425 *
426 * @param[in] vpdFilePath - EEPROM path of the FRU.
427 * @param[out] vpdVector - VPD in vector form.
428 * @param[in] vpdStartOffset - Offset of VPD data in EEPROM.
429 */
430inline void getVpdDataInVector(const std::string& vpdFilePath,
431 types::BinaryVector& vpdVector,
432 size_t& vpdStartOffset)
433{
434 try
435 {
436 std::fstream vpdFileStream;
437 vpdFileStream.exceptions(
438 std::ifstream::badbit | std::ifstream::failbit);
439 vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::binary);
440 auto vpdSizeToRead = std::min(std::filesystem::file_size(vpdFilePath),
441 static_cast<uintmax_t>(65504));
442 vpdVector.resize(vpdSizeToRead);
443
444 vpdFileStream.seekg(vpdStartOffset, std::ios_base::beg);
445 vpdFileStream.read(reinterpret_cast<char*>(&vpdVector[0]),
446 vpdSizeToRead);
447
448 vpdVector.resize(vpdFileStream.gcount());
449 vpdFileStream.clear(std::ios_base::eofbit);
450 }
451 catch (const std::ifstream::failure& fail)
452 {
453 std::cerr << "Exception in file handling [" << vpdFilePath
454 << "] error : " << fail.what();
455 throw;
456 }
457}
458
459/**
460 * @brief An API to get D-bus representation of given VPD keyword.
461 *
462 * @param[in] i_keywordName - VPD keyword name.
463 *
464 * @return D-bus representation of given keyword.
465 */
466inline std::string getDbusPropNameForGivenKw(const std::string& i_keywordName)
467{
468 // Check for "#" prefixed VPD keyword.
469 if ((i_keywordName.size() == vpd::constants::TWO_BYTES) &&
470 (i_keywordName.at(0) == constants::POUND_KW))
471 {
472 // D-bus doesn't support "#". Replace "#" with "PD_" for those "#"
473 // prefixed keywords.
474 return (std::string(constants::POUND_KW_PREFIX) +
475 i_keywordName.substr(1));
476 }
477
478 // Return the keyword name back, if D-bus representation is same as the VPD
479 // keyword name.
480 return i_keywordName;
481}
482
483/**
484 * @brief API to find CCIN in parsed VPD map.
485 *
486 * Few FRUs need some special handling. To identify those FRUs CCIN are used.
487 * The API will check from parsed VPD map if the FRU is the one with desired
488 * CCIN.
489 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500490 * @param[in] i_JsonObject - Any JSON which contains CCIN tag to match.
491 * @param[in] i_parsedVpdMap - Parsed VPD map.
Souvik Roy815c6022025-02-20 00:54:24 -0600492 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500493 * @return True if found, false otherwise.
494 */
495inline bool findCcinInVpd(const nlohmann::json& i_JsonObject,
Souvik Roy815c6022025-02-20 00:54:24 -0600496 const types::VPDMapVariant& i_parsedVpdMap) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500497{
Souvik Roy815c6022025-02-20 00:54:24 -0600498 bool l_rc{false};
499 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500500 {
Souvik Roy815c6022025-02-20 00:54:24 -0600501 if (i_JsonObject.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500502 {
Souvik Roy815c6022025-02-20 00:54:24 -0600503 throw std::runtime_error("Json object is empty. Can't find CCIN");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500504 }
505
Souvik Roy815c6022025-02-20 00:54:24 -0600506 if (auto l_ipzVPDMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500507 {
Souvik Roy815c6022025-02-20 00:54:24 -0600508 auto l_itrToRec = (*l_ipzVPDMap).find("VINI");
509 if (l_itrToRec == (*l_ipzVPDMap).end())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500510 {
Souvik Roy815c6022025-02-20 00:54:24 -0600511 throw DataException(
512 "VINI record not found in parsed VPD. Can't find CCIN");
513 }
514
Souvik Roya55fcca2025-02-19 01:33:58 -0600515 std::string l_ccinFromVpd{
516 vpdSpecificUtility::getKwVal(l_itrToRec->second, "CC")};
Souvik Roy815c6022025-02-20 00:54:24 -0600517 if (l_ccinFromVpd.empty())
518 {
519 throw DataException(
520 "Empty CCIN value in VPD map. Can't find CCIN");
521 }
522
523 transform(l_ccinFromVpd.begin(), l_ccinFromVpd.end(),
524 l_ccinFromVpd.begin(), ::toupper);
525
526 for (std::string l_ccinValue : i_JsonObject["ccin"])
527 {
528 transform(l_ccinValue.begin(), l_ccinValue.end(),
529 l_ccinValue.begin(), ::toupper);
530
531 if (l_ccinValue.compare(l_ccinFromVpd) ==
532 constants::STR_CMP_SUCCESS)
533 {
534 // CCIN found
535 l_rc = true;
536 }
537 }
538
539 if (!l_rc)
540 {
541 logging::logMessage("No match found for CCIN");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500542 }
543 }
Souvik Roy815c6022025-02-20 00:54:24 -0600544 else
545 {
546 logging::logMessage("VPD type not supported. Can't find CCIN");
547 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500548 }
Souvik Roy815c6022025-02-20 00:54:24 -0600549 catch (const std::exception& l_ex)
550 {
551 const std::string l_errMsg{
552 "Failed to find CCIN in VPD. Error : " + std::string(l_ex.what())};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500553
Souvik Roy815c6022025-02-20 00:54:24 -0600554 if (typeid(l_ex) == std::type_index(typeid(DataException)))
555 {
556 EventLogger::createSyncPel(
557 types::ErrorType::InvalidVpdMessage,
558 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
559 l_errMsg, std::nullopt, std::nullopt, std::nullopt,
560 std::nullopt);
561 }
562
563 logging::logMessage(l_errMsg);
564 }
565 return l_rc;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500566}
567
568/**
569 * @brief API to reset data of a FRU populated under PIM.
570 *
571 * This API resets the data for particular interfaces of a FRU under PIM.
572 *
573 * @param[in] i_objectPath - DBus object path of the FRU.
574 * @param[in] io_interfaceMap - Interface and its properties map.
575 */
576inline void resetDataUnderPIM(const std::string& i_objectPath,
577 types::InterfaceMap& io_interfaceMap)
578{
579 try
580 {
581 std::array<const char*, 0> l_interfaces;
582 const types::MapperGetObject& l_getObjectMap =
583 dbusUtility::getObjectMap(i_objectPath, l_interfaces);
584
585 const std::vector<std::string>& l_vpdRelatedInterfaces{
586 constants::operationalStatusInf, constants::inventoryItemInf,
Anupama B Rffa488f2025-05-22 07:34:00 -0500587 constants::assetInf, constants::vpdCollectionInterface};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500588
589 for (const auto& [l_service, l_interfaceList] : l_getObjectMap)
590 {
591 if (l_service.compare(constants::pimServiceName) !=
592 constants::STR_CMP_SUCCESS)
593 {
594 continue;
595 }
596
597 for (const auto& l_interface : l_interfaceList)
598 {
599 if ((l_interface.find(constants::ipzVpdInf) !=
600 std::string::npos) ||
601 ((std::find(l_vpdRelatedInterfaces.begin(),
602 l_vpdRelatedInterfaces.end(), l_interface)) !=
603 l_vpdRelatedInterfaces.end()))
604 {
605 const types::PropertyMap& l_propertyValueMap =
606 dbusUtility::getPropertyMap(l_service, i_objectPath,
607 l_interface);
608
609 types::PropertyMap l_propertyMap;
610
611 for (const auto& l_aProperty : l_propertyValueMap)
612 {
613 const std::string& l_propertyName = l_aProperty.first;
614 const auto& l_propertyValue = l_aProperty.second;
615
616 if (std::holds_alternative<types::BinaryVector>(
617 l_propertyValue))
618 {
619 l_propertyMap.emplace(l_propertyName,
620 types::BinaryVector{});
621 }
622 else if (std::holds_alternative<std::string>(
623 l_propertyValue))
624 {
Anupama B R5cd1b2d2025-08-05 04:57:40 -0500625 if (l_propertyName.compare("Status") ==
Anupama B Rffa488f2025-05-22 07:34:00 -0500626 constants::STR_CMP_SUCCESS)
627 {
628 l_propertyMap.emplace(
629 l_propertyName,
630 constants::vpdCollectionNotStarted);
Anupama B R4c65fcd2025-09-01 08:09:00 -0500631 l_propertyMap.emplace("StartTime", 0);
632 l_propertyMap.emplace("CompletedTime", 0);
Anupama B Rffa488f2025-05-22 07:34:00 -0500633 }
634 else
635 {
636 l_propertyMap.emplace(l_propertyName,
637 std::string{});
638 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500639 }
640 else if (std::holds_alternative<bool>(l_propertyValue))
641 {
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500642 if (l_propertyName.compare("Present") ==
643 constants::STR_CMP_SUCCESS)
644 {
645 l_propertyMap.emplace(l_propertyName, false);
646 }
Rekha Aparna4f722062025-05-02 03:39:32 -0500647 else if (l_propertyName.compare("Functional") ==
648 constants::STR_CMP_SUCCESS)
649 {
650 // Since FRU is not present functional property
651 // is considered as true.
652 l_propertyMap.emplace(l_propertyName, true);
653 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500654 }
655 }
656 io_interfaceMap.emplace(l_interface,
657 std::move(l_propertyMap));
658 }
659 }
660 }
661 }
662 catch (const std::exception& l_ex)
663 {
664 logging::logMessage("Failed to remove VPD for FRU: " + i_objectPath +
665 " with error: " + std::string(l_ex.what()));
666 }
667}
Sunny Srivastava78c91072025-02-05 14:09:50 +0530668
669/**
670 * @brief API to detect pass1 planar type.
671 *
672 * Based on HW version and IM keyword, This API detects is it is a pass1 planar
673 * or not.
674 *
675 * @return True if pass 1 planar, false otherwise.
676 */
Souvik Roy094a7352025-02-20 01:31:45 -0600677inline bool isPass1Planar() noexcept
Sunny Srivastava78c91072025-02-05 14:09:50 +0530678{
Souvik Roy094a7352025-02-20 01:31:45 -0600679 bool l_rc{false};
680 try
Sunny Srivastava78c91072025-02-05 14:09:50 +0530681 {
Souvik Roy094a7352025-02-20 01:31:45 -0600682 auto l_retVal = dbusUtility::readDbusProperty(
683 constants::pimServiceName, constants::systemVpdInvPath,
684 constants::viniInf, constants::kwdHW);
Sunny Srivastava78c91072025-02-05 14:09:50 +0530685
Souvik Roy094a7352025-02-20 01:31:45 -0600686 auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal);
687
688 l_retVal = dbusUtility::readDbusProperty(
689 constants::pimServiceName, constants::systemInvPath,
690 constants::vsbpInf, constants::kwdIM);
691
692 auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal);
693
694 if (l_hwVer && l_imValue)
Sunny Srivastava78c91072025-02-05 14:09:50 +0530695 {
Souvik Roy094a7352025-02-20 01:31:45 -0600696 if (l_hwVer->size() != constants::VALUE_2)
Sunny Srivastava78c91072025-02-05 14:09:50 +0530697 {
Souvik Roy094a7352025-02-20 01:31:45 -0600698 throw std::runtime_error("Invalid HW keyword length.");
699 }
700
701 if (l_imValue->size() != constants::VALUE_4)
702 {
703 throw std::runtime_error("Invalid IM keyword length.");
704 }
705
706 const types::BinaryVector l_everest{80, 00, 48, 00};
707 const types::BinaryVector l_fuji{96, 00, 32, 00};
708
709 if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji))
710 {
711 if ((*l_hwVer).at(1) < constants::VALUE_21)
712 {
713 l_rc = true;
714 }
715 }
716 else if ((*l_hwVer).at(1) < constants::VALUE_2)
717 {
718 l_rc = true;
Sunny Srivastava78c91072025-02-05 14:09:50 +0530719 }
720 }
Souvik Roy094a7352025-02-20 01:31:45 -0600721 }
722 catch (const std::exception& l_ex)
723 {
724 logging::logMessage("Failed to check for pass 1 planar. Error: " +
725 std::string(l_ex.what()));
Sunny Srivastava78c91072025-02-05 14:09:50 +0530726 }
727
Souvik Roy094a7352025-02-20 01:31:45 -0600728 return l_rc;
Sunny Srivastava78c91072025-02-05 14:09:50 +0530729}
Sunny Srivastavac6ef42d2025-02-19 19:17:10 +0530730
731/**
732 * @brief API to detect if system configuration is that of PowerVS system.
733 *
734 * @param[in] i_imValue - IM value of the system.
735 * @return true if it is PowerVS configuration, false otherwise.
736 */
737inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue)
738{
739 if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
740 {
741 return false;
742 }
743
744 // Should be a 0x5000XX series system.
745 if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
746 i_imValue.at(1) == constants::HEX_VALUE_00)
747 {
748 std::string l_imagePrefix = dbusUtility::getImagePrefix();
749
750 // Check image for 0x500030XX series.
751 if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
752 ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
753 (l_imagePrefix == constants::powerVsImagePrefix_NY)))
754 {
755 logging::logMessage("PowerVS configuration");
756 return true;
757 }
758
759 // Check image for 0X500010XX series.
760 if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
761 ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
762 (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
763 {
764 logging::logMessage("PowerVS configuration");
765 return true;
766 }
767 }
768 return false;
769}
Sunny Srivastavaf1dda762025-02-19 23:46:19 +0530770
771/**
772 * @brief API to get CCIN for a given FRU from DBus.
773 *
774 * The API reads the CCIN for a FRU based on its inventory path.
775 *
776 * @param[in] i_invObjPath - Inventory path of the FRU.
777 * @return CCIN of the FRU on success, empty string otherwise.
778 */
779inline std::string getCcinFromDbus(const std::string& i_invObjPath)
780{
781 try
782 {
783 if (i_invObjPath.empty())
784 {
785 throw std::runtime_error("Empty EEPROM path, can't read CCIN");
786 }
787
788 const auto& l_retValue = dbusUtility::readDbusProperty(
789 constants::pimServiceName, i_invObjPath, constants::viniInf,
790 constants::kwdCCIN);
791
792 auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
793 if (!l_ptrCcin || (*l_ptrCcin).size() != constants::VALUE_4)
794 {
795 throw DbusException("Invalid CCIN read from Dbus");
796 }
797
798 return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
799 }
800 catch (const std::exception& l_ex)
801 {
802 logging::logMessage(l_ex.what());
803 return std::string{};
804 }
805}
Sunny Srivastavaecfaa212025-03-24 13:02:13 +0530806
807/**
808 * @brief API to check if the current running image is a powerVS image.
809 *
810 * @return true if it is PowerVS image, false otherwise.
811 */
812inline bool isPowerVsImage()
813{
814 std::string l_imagePrefix = dbusUtility::getImagePrefix();
815
816 if ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
817 (l_imagePrefix == constants::powerVsImagePrefix_NY) ||
818 (l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
819 (l_imagePrefix == constants::powerVsImagePrefix_NZ))
820 {
821 return true;
822 }
823 return false;
824}
Souvik Roy2ee8a212025-04-24 02:37:59 -0500825
826/**
827 * @brief API to sync keyword update to inherited FRUs.
828 *
829 * For a given keyword update on a EEPROM path, this API syncs the keyword
830 * update to all inherited FRUs' respective interface, property on PIM.
831 *
832 * @param[in] i_fruPath - EEPROM path of FRU.
833 * @param[in] i_paramsToWriteData - Input details.
834 * @param[in] i_sysCfgJsonObj - System config JSON.
835 *
836 */
837inline void updateKwdOnInheritedFrus(
838 const std::string& i_fruPath,
839 const types::WriteVpdParams& i_paramsToWriteData,
840 const nlohmann::json& i_sysCfgJsonObj) noexcept
841{
842 try
843 {
844 if (!i_sysCfgJsonObj.contains("frus"))
845 {
846 throw std::runtime_error("Mandatory tag(s) missing from JSON");
847 }
848
849 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
850 {
851 throw std::runtime_error(
852 "VPD path [" + i_fruPath + "] not found in system config JSON");
853 }
854
855 const types::IpzData* l_ipzData =
856 std::get_if<types::IpzData>(&i_paramsToWriteData);
857
858 if (!l_ipzData)
859 {
860 throw std::runtime_error("Unsupported VPD type");
861 }
862 // iterate through all inventory paths for given EEPROM path,
863 // except the base FRU.
864 // if for an inventory path, "inherit" tag is true,
865 // update the inventory path's com.ibm.ipzvpd.<record>,keyword
866 // property
867
868 types::ObjectMap l_objectInterfaceMap;
869
870 auto l_populateInterfaceMap =
871 [&l_objectInterfaceMap,
872 &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) {
873 // update inherited FRUs only
874 if (l_Fru.value("inherit", true))
875 {
876 l_objectInterfaceMap.emplace(
877 sdbusplus::message::object_path{l_Fru["inventoryPath"]},
878 types::InterfaceMap{
879 {std::string{constants::ipzVpdInf +
880 std::get<0>(*l_ipzData)},
881 types::PropertyMap{{std::get<1>(*l_ipzData),
882 std::get<2>(*l_ipzData)}}}});
883 }
884 };
885
886 // iterate through all FRUs except the base FRU
887 std::for_each(
888 i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1,
889 i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap);
890
891 if (!l_objectInterfaceMap.empty())
892 {
893 // notify PIM
894 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
895 {
896 throw std::runtime_error(
897 "Call to PIM failed for VPD file " + i_fruPath);
898 }
899 }
900 }
901 catch (const std::exception& l_ex)
902 {
903 logging::logMessage(
904 "Failed to sync keyword update to inherited FRUs of FRU [" +
905 i_fruPath + "]. Error: " + std::string(l_ex.what()));
906 }
907}
Souvik Roy96ebe962025-04-29 04:01:07 -0500908
909/**
910 * @brief API to get common interface(s) properties corresponding to given
911 * record and keyword.
912 *
913 * For a given record and keyword, this API finds the corresponding common
914 * interfaces(s) properties from the system config JSON and populates an
915 * interface map with the respective properties and values.
916 *
917 * @param[in] i_paramsToWriteData - Input details.
918 * @param[in] i_commonInterfaceJson - Common interface JSON object.
919 *
920 * @return Returns a map of common interface(s) and properties corresponding to
921 * the record and keyword. An empty map is returned if no such common
922 * interface(s) and properties are found.
923 */
924inline types::InterfaceMap getCommonInterfaceProperties(
925 const types::WriteVpdParams& i_paramsToWriteData,
926 const nlohmann::json& i_commonInterfaceJson) noexcept
927{
928 types::InterfaceMap l_interfaceMap;
929 try
930 {
931 const types::IpzData* l_ipzData =
932 std::get_if<types::IpzData>(&i_paramsToWriteData);
933
934 if (!l_ipzData)
935 {
936 throw std::runtime_error("Invalid VPD type");
937 }
938
939 auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData),
940 &l_interfaceMap](
941 const auto& l_interfacesPropPair) {
942 // find matching property value pair
943 const auto l_matchPropValuePairIt = std::find_if(
944 l_interfacesPropPair.value().items().begin(),
945 l_interfacesPropPair.value().items().end(),
946 [&l_ipzData](const auto& l_propValuePair) {
947 return (l_propValuePair.value().value("recordName", "") ==
948 std::get<0>(*l_ipzData) &&
949 l_propValuePair.value().value("keywordName", "") ==
950 std::get<1>(*l_ipzData));
951 });
952
953 if (l_matchPropValuePairIt !=
954 l_interfacesPropPair.value().items().end())
955 {
956 // add property map to interface map
957 l_interfaceMap.emplace(
958 l_interfacesPropPair.key(),
959 types::PropertyMap{
960 {l_matchPropValuePairIt.key(),
961 vpdSpecificUtility::encodeKeyword(
962 std::string(std::get<2>(*l_ipzData).begin(),
963 std::get<2>(*l_ipzData).end()),
964 l_matchPropValuePairIt.value().value("encoding",
965 ""))}});
966 }
967 };
968
Sunny Srivastava89fbd2d2025-09-03 02:03:48 -0500969 if (!i_commonInterfaceJson.empty())
970 {
971 // iterate through all common interfaces and populate interface map
972 std::for_each(i_commonInterfaceJson.items().begin(),
973 i_commonInterfaceJson.items().end(),
974 l_populateInterfaceMap);
975 }
Souvik Roy96ebe962025-04-29 04:01:07 -0500976 }
977 catch (const std::exception& l_ex)
978 {
979 logging::logMessage(
980 "Failed to find common interface properties. Error: " +
981 std::string(l_ex.what()));
982 }
983 return l_interfaceMap;
984}
985
986/**
987 * @brief API to update common interface(s) properties when keyword is updated.
988 *
989 * For a given keyword update on a EEPROM path, this API syncs the keyword
990 * update to respective common interface(s) properties of the base FRU and all
991 * inherited FRUs.
992 *
993 * @param[in] i_fruPath - EEPROM path of FRU.
994 * @param[in] i_paramsToWriteData - Input details.
995 * @param[in] i_sysCfgJsonObj - System config JSON.
996 *
997 */
998inline void updateCiPropertyOfInheritedFrus(
999 const std::string& i_fruPath,
1000 const types::WriteVpdParams& i_paramsToWriteData,
1001 const nlohmann::json& i_sysCfgJsonObj) noexcept
1002{
1003 try
1004 {
1005 if (!i_sysCfgJsonObj.contains("commonInterfaces"))
1006 {
1007 // no common interfaces in JSON, nothing to do
1008 return;
1009 }
1010
1011 if (!i_sysCfgJsonObj.contains("frus"))
1012 {
1013 throw std::runtime_error("Mandatory tag(s) missing from JSON");
1014 }
1015
1016 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1017 {
1018 throw std::runtime_error(
1019 "VPD path [" + i_fruPath + "] not found in system config JSON");
1020 }
1021
1022 if (!std::get_if<types::IpzData>(&i_paramsToWriteData))
1023 {
1024 throw std::runtime_error("Unsupported VPD type");
1025 }
1026
1027 // iterate through all inventory paths for given EEPROM path,
1028 // if for an inventory path, "inherit" tag is true,
1029 // update the inventory path's com.ibm.ipzvpd.<record>,keyword
1030 // property
1031
1032 types::ObjectMap l_objectInterfaceMap;
1033
1034 const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties(
1035 i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"]);
1036
1037 if (l_interfaceMap.empty())
1038 {
1039 // nothing to do
1040 return;
1041 }
1042
1043 auto l_populateObjectInterfaceMap =
1044 [&l_objectInterfaceMap, &l_interfaceMap = std::as_const(
1045 l_interfaceMap)](const auto& l_Fru) {
1046 if (l_Fru.value("inherit", true) &&
1047 l_Fru.contains("inventoryPath"))
1048 {
1049 l_objectInterfaceMap.emplace(
1050 sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1051 l_interfaceMap);
1052 }
1053 };
1054
1055 std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(),
1056 i_sysCfgJsonObj["frus"][i_fruPath].end(),
1057 l_populateObjectInterfaceMap);
1058
1059 if (!l_objectInterfaceMap.empty())
1060 {
1061 // notify PIM
1062 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1063 {
1064 throw std::runtime_error(
1065 "Call to PIM failed for VPD file " + i_fruPath);
1066 }
1067 }
1068 }
1069 catch (const std::exception& l_ex)
1070 {
1071 logging::logMessage(
1072 "Failed to update common interface properties of FRU [" +
1073 i_fruPath + "]. Error: " + std::string(l_ex.what()));
1074 }
1075}
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001076
1077/**
Rekha Aparnaa5be7222025-09-01 00:42:22 -05001078 * @brief API to get error code message.
1079 *
1080 * @param[in] i_errCode - error code.
1081 *
1082 * @return Error message set for that error code. Otherwise empty
1083 * string.
1084 */
1085inline std::string getErrCodeMsg(const uint16_t& i_errCode)
1086{
Sunny Srivastavaad7e25e2025-09-04 09:43:21 -05001087 if (errorCodeMap.find(i_errCode) != errorCodeMap.end())
Rekha Aparnaa5be7222025-09-01 00:42:22 -05001088 {
Sunny Srivastavaad7e25e2025-09-04 09:43:21 -05001089 return errorCodeMap.at(i_errCode);
Rekha Aparnaa5be7222025-09-01 00:42:22 -05001090 }
1091
1092 return std::string{};
1093}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001094} // namespace vpdSpecificUtility
1095} // namespace vpd