blob: ced7ef1135b95fea3beaf26fb7cc0869598f80cb [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#pragma once
2
3#include "config.h"
4
5#include "constants.hpp"
Souvik Roy815c6022025-02-20 00:54:24 -06006#include "event_logger.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05007#include "exceptions.hpp"
8#include "logger.hpp"
9#include "types.hpp"
10
11#include <nlohmann/json.hpp>
12#include <utility/common_utility.hpp>
13#include <utility/dbus_utility.hpp>
14
15#include <filesystem>
16#include <fstream>
17#include <regex>
Souvik Roy815c6022025-02-20 00:54:24 -060018#include <typeindex>
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050019
20namespace vpd
21{
22namespace vpdSpecificUtility
23{
24/**
25 * @brief API to generate file name for bad VPD.
26 *
27 * For i2c eeproms - the pattern of the vpd-name will be
28 * i2c-<bus-number>-<eeprom-address>.
29 * For spi eeproms - the pattern of the vpd-name will be spi-<spi-number>.
30 *
Souvik Roy8fc12522025-02-19 01:28:38 -060031 * @param[in] i_vpdFilePath - file path of the vpd.
32 *
33 * @return On success, returns generated file name, otherwise returns empty
34 * string.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050035 */
Souvik Roy8fc12522025-02-19 01:28:38 -060036inline std::string generateBadVPDFileName(
37 const std::string& i_vpdFilePath) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050038{
Souvik Roy0f370432025-04-08 01:55:58 -050039 std::string l_badVpdFileName{constants::badVpdDir};
Souvik Roy8fc12522025-02-19 01:28:38 -060040 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050041 {
Souvik Roy8fc12522025-02-19 01:28:38 -060042 if (i_vpdFilePath.find("i2c") != std::string::npos)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050043 {
Souvik Roy8fc12522025-02-19 01:28:38 -060044 l_badVpdFileName += "i2c-";
45 std::regex l_i2cPattern("(at24/)([0-9]+-[0-9]+)\\/");
46 std::smatch l_match;
47 if (std::regex_search(i_vpdFilePath, l_match, l_i2cPattern))
48 {
49 l_badVpdFileName += l_match.str(2);
50 }
51 }
52 else if (i_vpdFilePath.find("spi") != std::string::npos)
53 {
54 std::regex l_spiPattern("((spi)[0-9]+)(.0)");
55 std::smatch l_match;
56 if (std::regex_search(i_vpdFilePath, l_match, l_spiPattern))
57 {
58 l_badVpdFileName += l_match.str(1);
59 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050060 }
61 }
Souvik Roy8fc12522025-02-19 01:28:38 -060062 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050063 {
Souvik Roy8fc12522025-02-19 01:28:38 -060064 l_badVpdFileName.clear();
65 logging::logMessage("Failed to generate bad VPD file name for [" +
66 i_vpdFilePath + "]. Error: " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050067 }
Souvik Roy8fc12522025-02-19 01:28:38 -060068 return l_badVpdFileName;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050069}
70
71/**
72 * @brief API which dumps the broken/bad vpd in a directory.
73 * When the vpd is bad, this API places the bad vpd file inside
Souvik Roy0f370432025-04-08 01:55:58 -050074 * "/var/lib/vpd/dumps" in BMC, in order to collect bad VPD data as a part of
75 * user initiated BMC dump.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050076 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050077 *
Souvik Roy8fc12522025-02-19 01:28:38 -060078 * @param[in] i_vpdFilePath - vpd file path
79 * @param[in] i_vpdVector - vpd vector
80 *
81 * @return On success returns 0, otherwise returns -1.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050082 */
Souvik Roy8fc12522025-02-19 01:28:38 -060083inline int dumpBadVpd(const std::string& i_vpdFilePath,
84 const types::BinaryVector& i_vpdVector) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050085{
Souvik Roy8fc12522025-02-19 01:28:38 -060086 int l_rc{constants::FAILURE};
87 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050088 {
Souvik Roy0f370432025-04-08 01:55:58 -050089 std::filesystem::create_directory(constants::badVpdDir);
Souvik Roy8fc12522025-02-19 01:28:38 -060090 auto l_badVpdPath = generateBadVPDFileName(i_vpdFilePath);
91
92 if (l_badVpdPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050093 {
Souvik Roy8fc12522025-02-19 01:28:38 -060094 throw std::runtime_error("Failed to generate bad VPD file name");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050095 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050096
Souvik Roy8fc12522025-02-19 01:28:38 -060097 if (std::filesystem::exists(l_badVpdPath))
98 {
99 std::error_code l_ec;
100 std::filesystem::remove(l_badVpdPath, l_ec);
101 if (l_ec) // error code
102 {
103 const std::string l_errorMsg{
104 "Error removing the existing broken vpd in " +
105 l_badVpdPath +
106 ". Error code : " + std::to_string(l_ec.value()) +
107 ". Error message : " + l_ec.message()};
108
109 throw std::runtime_error(l_errorMsg);
110 }
111 }
112
113 std::ofstream l_badVpdFileStream(l_badVpdPath, std::ofstream::binary);
114 if (!l_badVpdFileStream.is_open())
115 {
Souvik Roy0f370432025-04-08 01:55:58 -0500116 const std::string l_errorMsg{
117 "Failed to open bad vpd file path [" + l_badVpdPath +
118 "]. Unable to dump the broken/bad vpd file."};
119
120 throw std::runtime_error(l_errorMsg);
Souvik Roy8fc12522025-02-19 01:28:38 -0600121 }
122
123 l_badVpdFileStream.write(
124 reinterpret_cast<const char*>(i_vpdVector.data()),
125 i_vpdVector.size());
126
127 l_rc = constants::SUCCESS;
128 }
129 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500130 {
Souvik Roy8fc12522025-02-19 01:28:38 -0600131 logging::logMessage("Failed to dump bad VPD for [" + i_vpdFilePath +
132 "]. Error: " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500133 }
Souvik Roy8fc12522025-02-19 01:28:38 -0600134 return l_rc;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500135}
136
137/**
138 * @brief An API to read value of a keyword.
139 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500140 *
Souvik Roya55fcca2025-02-19 01:33:58 -0600141 * @param[in] i_kwdValueMap - A map having Kwd value pair.
142 * @param[in] i_kwd - keyword name.
143 *
144 * @return On success returns value of the keyword read from map, otherwise
145 * returns empty string.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500146 */
Souvik Roya55fcca2025-02-19 01:33:58 -0600147inline std::string getKwVal(const types::IPZKwdValueMap& i_kwdValueMap,
148 const std::string& i_kwd) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500149{
Souvik Roya55fcca2025-02-19 01:33:58 -0600150 std::string l_kwdValue;
151 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500152 {
Souvik Roya55fcca2025-02-19 01:33:58 -0600153 if (i_kwd.empty())
154 {
155 throw std::runtime_error("Invalid parameters");
156 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500157
Souvik Roya55fcca2025-02-19 01:33:58 -0600158 auto l_itrToKwd = i_kwdValueMap.find(i_kwd);
159 if (l_itrToKwd != i_kwdValueMap.end())
160 {
161 l_kwdValue = l_itrToKwd->second;
162 }
163 else
164 {
165 throw std::runtime_error("Keyword not found");
166 }
167 }
168 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500169 {
Souvik Roya55fcca2025-02-19 01:33:58 -0600170 logging::logMessage("Failed to get value for keyword [" + i_kwd +
171 "]. Error : " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500172 }
Souvik Roya55fcca2025-02-19 01:33:58 -0600173 return l_kwdValue;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500174}
175
176/**
177 * @brief An API to process encoding of a keyword.
178 *
Souvik Royf277d6a2025-02-20 00:08:43 -0600179 * @param[in] i_keyword - Keyword to be processed.
180 * @param[in] i_encoding - Type of encoding.
181 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500182 * @return Value after being processed for encoded type.
183 */
Souvik Royf277d6a2025-02-20 00:08:43 -0600184inline std::string encodeKeyword(const std::string& i_keyword,
185 const std::string& i_encoding) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500186{
187 // Default value is keyword value
Souvik Royf277d6a2025-02-20 00:08:43 -0600188 std::string l_result(i_keyword.begin(), i_keyword.end());
189 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500190 {
Souvik Royf277d6a2025-02-20 00:08:43 -0600191 if (i_encoding == "MAC")
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500192 {
Souvik Royf277d6a2025-02-20 00:08:43 -0600193 l_result.clear();
194 size_t l_firstByte = i_keyword[0];
195 l_result += commonUtility::toHex(l_firstByte >> 4);
196 l_result += commonUtility::toHex(l_firstByte & 0x0f);
197 for (size_t i = 1; i < i_keyword.size(); ++i)
198 {
199 l_result += ":";
200 l_result += commonUtility::toHex(i_keyword[i] >> 4);
201 l_result += commonUtility::toHex(i_keyword[i] & 0x0f);
202 }
203 }
204 else if (i_encoding == "DATE")
205 {
206 // Date, represent as
207 // <year>-<month>-<day> <hour>:<min>
208 l_result.clear();
209 static constexpr uint8_t skipPrefix = 3;
210
211 auto strItr = i_keyword.begin();
212 advance(strItr, skipPrefix);
213 for_each(strItr, i_keyword.end(),
214 [&l_result](size_t c) { l_result += c; });
215
216 l_result.insert(constants::BD_YEAR_END, 1, '-');
217 l_result.insert(constants::BD_MONTH_END, 1, '-');
218 l_result.insert(constants::BD_DAY_END, 1, ' ');
219 l_result.insert(constants::BD_HOUR_END, 1, ':');
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500220 }
221 }
Souvik Royf277d6a2025-02-20 00:08:43 -0600222 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500223 {
Souvik Royf277d6a2025-02-20 00:08:43 -0600224 l_result.clear();
225 logging::logMessage("Failed to encode keyword [" + i_keyword +
226 "]. Error: " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500227 }
228
Souvik Royf277d6a2025-02-20 00:08:43 -0600229 return l_result;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500230}
231
232/**
233 * @brief Helper function to insert or merge in map.
234 *
235 * This method checks in an interface if the given interface exists. If the
236 * interface key already exists, property map is inserted corresponding to it.
237 * If the key does'nt exist then given interface and property map pair is newly
238 * created. If the property present in propertymap already exist in the
239 * InterfaceMap, then the new property value is ignored.
240 *
Souvik Royfa47e6c2025-02-20 00:17:25 -0600241 * @param[in,out] io_map - Interface map.
242 * @param[in] i_interface - Interface to be processed.
243 * @param[in] i_propertyMap - new property map that needs to be emplaced.
244 *
245 * @return On success returns 0, otherwise returns -1.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500246 */
Souvik Royfa47e6c2025-02-20 00:17:25 -0600247inline int insertOrMerge(types::InterfaceMap& io_map,
248 const std::string& i_interface,
249 types::PropertyMap&& i_propertyMap) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500250{
Souvik Royfa47e6c2025-02-20 00:17:25 -0600251 int l_rc{constants::FAILURE};
252 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500253 {
Souvik Royfa47e6c2025-02-20 00:17:25 -0600254 if (io_map.find(i_interface) != io_map.end())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500255 {
Souvik Royfa47e6c2025-02-20 00:17:25 -0600256 auto& l_prop = io_map.at(i_interface);
257 std::for_each(i_propertyMap.begin(), i_propertyMap.end(),
258 [&l_prop](auto l_keyValue) {
259 l_prop[l_keyValue.first] = l_keyValue.second;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500260 });
261 }
Souvik Royfa47e6c2025-02-20 00:17:25 -0600262 else
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500263 {
Souvik Royfa47e6c2025-02-20 00:17:25 -0600264 io_map.emplace(i_interface, i_propertyMap);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500265 }
Souvik Royfa47e6c2025-02-20 00:17:25 -0600266
267 l_rc = constants::SUCCESS;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500268 }
Souvik Royfa47e6c2025-02-20 00:17:25 -0600269 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500270 {
Souvik Royfa47e6c2025-02-20 00:17:25 -0600271 // ToDo:: Log PEL
272 logging::logMessage(
273 "Inserting properties into interface[" + i_interface +
274 "] map failed, reason: " + std::string(l_ex.what()));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500275 }
Souvik Royfa47e6c2025-02-20 00:17:25 -0600276 return l_rc;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500277}
278
279/**
280 * @brief API to expand unpanded location code.
281 *
282 * Note: The API handles all the exception internally, in case of any error
283 * unexpanded location code will be returned as it is.
284 *
285 * @param[in] unexpandedLocationCode - Unexpanded location code.
286 * @param[in] parsedVpdMap - Parsed VPD map.
287 * @return Expanded location code. In case of any error, unexpanded is returned
288 * as it is.
289 */
Patrick Williams43fedab2025-02-03 14:28:05 -0500290inline std::string getExpandedLocationCode(
291 const std::string& unexpandedLocationCode,
292 const types::VPDMapVariant& parsedVpdMap)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500293{
294 auto expanded{unexpandedLocationCode};
295
296 try
297 {
298 // Expanded location code is formed by combining two keywords
299 // depending on type in unexpanded one. Second one is always "SE".
300 std::string kwd1, kwd2{constants::kwdSE};
301
302 // interface to search for required keywords;
303 std::string kwdInterface;
304
305 // record which holds the required keywords.
306 std::string recordName;
307
308 auto pos = unexpandedLocationCode.find("fcs");
309 if (pos != std::string::npos)
310 {
311 kwd1 = constants::kwdFC;
312 kwdInterface = constants::vcenInf;
313 recordName = constants::recVCEN;
314 }
315 else
316 {
317 pos = unexpandedLocationCode.find("mts");
318 if (pos != std::string::npos)
319 {
320 kwd1 = constants::kwdTM;
321 kwdInterface = constants::vsysInf;
322 recordName = constants::recVSYS;
323 }
324 else
325 {
326 throw std::runtime_error(
327 "Error detecting type of unexpanded location code.");
328 }
329 }
330
331 std::string firstKwdValue, secondKwdValue;
332
333 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap);
334 ipzVpdMap && (*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
335 {
336 auto itrToVCEN = (*ipzVpdMap).find(recordName);
Souvik Roya55fcca2025-02-19 01:33:58 -0600337 firstKwdValue = getKwVal(itrToVCEN->second, kwd1);
338 if (firstKwdValue.empty())
339 {
340 throw std::runtime_error(
341 "Failed to get value for keyword [" + kwd1 + "]");
342 }
343
344 secondKwdValue = getKwVal(itrToVCEN->second, kwd2);
345 if (secondKwdValue.empty())
346 {
347 throw std::runtime_error(
348 "Failed to get value for keyword [" + kwd2 + "]");
349 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500350 }
351 else
352 {
353 std::array<const char*, 1> interfaceList = {kwdInterface.c_str()};
354
355 types::MapperGetObject mapperRetValue = dbusUtility::getObjectMap(
356 std::string(constants::systemVpdInvPath), interfaceList);
357
358 if (mapperRetValue.empty())
359 {
360 throw std::runtime_error("Mapper failed to get service");
361 }
362
363 const std::string& serviceName = std::get<0>(mapperRetValue.at(0));
364
365 auto retVal = dbusUtility::readDbusProperty(
366 serviceName, std::string(constants::systemVpdInvPath),
367 kwdInterface, kwd1);
368
369 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
370 {
371 firstKwdValue.assign(
372 reinterpret_cast<const char*>(kwdVal->data()),
373 kwdVal->size());
374 }
375 else
376 {
377 throw std::runtime_error(
378 "Failed to read value of " + kwd1 + " from Bus");
379 }
380
381 retVal = dbusUtility::readDbusProperty(
382 serviceName, std::string(constants::systemVpdInvPath),
383 kwdInterface, kwd2);
384
385 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
386 {
387 secondKwdValue.assign(
388 reinterpret_cast<const char*>(kwdVal->data()),
389 kwdVal->size());
390 }
391 else
392 {
393 throw std::runtime_error(
394 "Failed to read value of " + kwd2 + " from Bus");
395 }
396 }
397
398 if (unexpandedLocationCode.find("fcs") != std::string::npos)
399 {
400 // TODO: See if ND0 can be placed in the JSON
401 expanded.replace(
402 pos, 3, firstKwdValue.substr(0, 4) + ".ND0." + secondKwdValue);
403 }
404 else
405 {
406 replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
407 expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
408 }
409 }
410 catch (const std::exception& ex)
411 {
412 logging::logMessage("Failed to expand location code with exception: " +
413 std::string(ex.what()));
414 }
415
416 return expanded;
417}
418
419/**
420 * @brief An API to get VPD in a vector.
421 *
422 * The vector is required by the respective parser to fill the VPD map.
423 * Note: API throws exception in case of failure. Caller needs to handle.
424 *
425 * @param[in] vpdFilePath - EEPROM path of the FRU.
426 * @param[out] vpdVector - VPD in vector form.
427 * @param[in] vpdStartOffset - Offset of VPD data in EEPROM.
428 */
429inline void getVpdDataInVector(const std::string& vpdFilePath,
430 types::BinaryVector& vpdVector,
431 size_t& vpdStartOffset)
432{
433 try
434 {
435 std::fstream vpdFileStream;
436 vpdFileStream.exceptions(
437 std::ifstream::badbit | std::ifstream::failbit);
438 vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::binary);
439 auto vpdSizeToRead = std::min(std::filesystem::file_size(vpdFilePath),
440 static_cast<uintmax_t>(65504));
441 vpdVector.resize(vpdSizeToRead);
442
443 vpdFileStream.seekg(vpdStartOffset, std::ios_base::beg);
444 vpdFileStream.read(reinterpret_cast<char*>(&vpdVector[0]),
445 vpdSizeToRead);
446
447 vpdVector.resize(vpdFileStream.gcount());
448 vpdFileStream.clear(std::ios_base::eofbit);
449 }
450 catch (const std::ifstream::failure& fail)
451 {
452 std::cerr << "Exception in file handling [" << vpdFilePath
453 << "] error : " << fail.what();
454 throw;
455 }
456}
457
458/**
459 * @brief An API to get D-bus representation of given VPD keyword.
460 *
461 * @param[in] i_keywordName - VPD keyword name.
462 *
463 * @return D-bus representation of given keyword.
464 */
465inline std::string getDbusPropNameForGivenKw(const std::string& i_keywordName)
466{
467 // Check for "#" prefixed VPD keyword.
468 if ((i_keywordName.size() == vpd::constants::TWO_BYTES) &&
469 (i_keywordName.at(0) == constants::POUND_KW))
470 {
471 // D-bus doesn't support "#". Replace "#" with "PD_" for those "#"
472 // prefixed keywords.
473 return (std::string(constants::POUND_KW_PREFIX) +
474 i_keywordName.substr(1));
475 }
476
477 // Return the keyword name back, if D-bus representation is same as the VPD
478 // keyword name.
479 return i_keywordName;
480}
481
482/**
483 * @brief API to find CCIN in parsed VPD map.
484 *
485 * Few FRUs need some special handling. To identify those FRUs CCIN are used.
486 * The API will check from parsed VPD map if the FRU is the one with desired
487 * CCIN.
488 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500489 * @param[in] i_JsonObject - Any JSON which contains CCIN tag to match.
490 * @param[in] i_parsedVpdMap - Parsed VPD map.
Souvik Roy815c6022025-02-20 00:54:24 -0600491 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500492 * @return True if found, false otherwise.
493 */
494inline bool findCcinInVpd(const nlohmann::json& i_JsonObject,
Souvik Roy815c6022025-02-20 00:54:24 -0600495 const types::VPDMapVariant& i_parsedVpdMap) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500496{
Souvik Roy815c6022025-02-20 00:54:24 -0600497 bool l_rc{false};
498 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500499 {
Souvik Roy815c6022025-02-20 00:54:24 -0600500 if (i_JsonObject.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500501 {
Souvik Roy815c6022025-02-20 00:54:24 -0600502 throw std::runtime_error("Json object is empty. Can't find CCIN");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500503 }
504
Souvik Roy815c6022025-02-20 00:54:24 -0600505 if (auto l_ipzVPDMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500506 {
Souvik Roy815c6022025-02-20 00:54:24 -0600507 auto l_itrToRec = (*l_ipzVPDMap).find("VINI");
508 if (l_itrToRec == (*l_ipzVPDMap).end())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500509 {
Souvik Roy815c6022025-02-20 00:54:24 -0600510 throw DataException(
511 "VINI record not found in parsed VPD. Can't find CCIN");
512 }
513
Souvik Roya55fcca2025-02-19 01:33:58 -0600514 std::string l_ccinFromVpd{
515 vpdSpecificUtility::getKwVal(l_itrToRec->second, "CC")};
Souvik Roy815c6022025-02-20 00:54:24 -0600516 if (l_ccinFromVpd.empty())
517 {
518 throw DataException(
519 "Empty CCIN value in VPD map. Can't find CCIN");
520 }
521
522 transform(l_ccinFromVpd.begin(), l_ccinFromVpd.end(),
523 l_ccinFromVpd.begin(), ::toupper);
524
525 for (std::string l_ccinValue : i_JsonObject["ccin"])
526 {
527 transform(l_ccinValue.begin(), l_ccinValue.end(),
528 l_ccinValue.begin(), ::toupper);
529
530 if (l_ccinValue.compare(l_ccinFromVpd) ==
531 constants::STR_CMP_SUCCESS)
532 {
533 // CCIN found
534 l_rc = true;
535 }
536 }
537
538 if (!l_rc)
539 {
540 logging::logMessage("No match found for CCIN");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500541 }
542 }
Souvik Roy815c6022025-02-20 00:54:24 -0600543 else
544 {
545 logging::logMessage("VPD type not supported. Can't find CCIN");
546 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500547 }
Souvik Roy815c6022025-02-20 00:54:24 -0600548 catch (const std::exception& l_ex)
549 {
550 const std::string l_errMsg{
551 "Failed to find CCIN in VPD. Error : " + std::string(l_ex.what())};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500552
Souvik Roy815c6022025-02-20 00:54:24 -0600553 if (typeid(l_ex) == std::type_index(typeid(DataException)))
554 {
555 EventLogger::createSyncPel(
556 types::ErrorType::InvalidVpdMessage,
557 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
558 l_errMsg, std::nullopt, std::nullopt, std::nullopt,
559 std::nullopt);
560 }
561
562 logging::logMessage(l_errMsg);
563 }
564 return l_rc;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500565}
566
567/**
568 * @brief API to reset data of a FRU populated under PIM.
569 *
570 * This API resets the data for particular interfaces of a FRU under PIM.
571 *
572 * @param[in] i_objectPath - DBus object path of the FRU.
573 * @param[in] io_interfaceMap - Interface and its properties map.
574 */
575inline void resetDataUnderPIM(const std::string& i_objectPath,
576 types::InterfaceMap& io_interfaceMap)
577{
578 try
579 {
580 std::array<const char*, 0> l_interfaces;
581 const types::MapperGetObject& l_getObjectMap =
582 dbusUtility::getObjectMap(i_objectPath, l_interfaces);
583
584 const std::vector<std::string>& l_vpdRelatedInterfaces{
585 constants::operationalStatusInf, constants::inventoryItemInf,
Anupama B Rffa488f2025-05-22 07:34:00 -0500586 constants::assetInf, constants::vpdCollectionInterface};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500587
588 for (const auto& [l_service, l_interfaceList] : l_getObjectMap)
589 {
590 if (l_service.compare(constants::pimServiceName) !=
591 constants::STR_CMP_SUCCESS)
592 {
593 continue;
594 }
595
596 for (const auto& l_interface : l_interfaceList)
597 {
598 if ((l_interface.find(constants::ipzVpdInf) !=
599 std::string::npos) ||
600 ((std::find(l_vpdRelatedInterfaces.begin(),
601 l_vpdRelatedInterfaces.end(), l_interface)) !=
602 l_vpdRelatedInterfaces.end()))
603 {
604 const types::PropertyMap& l_propertyValueMap =
605 dbusUtility::getPropertyMap(l_service, i_objectPath,
606 l_interface);
607
608 types::PropertyMap l_propertyMap;
609
610 for (const auto& l_aProperty : l_propertyValueMap)
611 {
612 const std::string& l_propertyName = l_aProperty.first;
613 const auto& l_propertyValue = l_aProperty.second;
614
615 if (std::holds_alternative<types::BinaryVector>(
616 l_propertyValue))
617 {
618 l_propertyMap.emplace(l_propertyName,
619 types::BinaryVector{});
620 }
621 else if (std::holds_alternative<std::string>(
622 l_propertyValue))
623 {
Anupama B Rffa488f2025-05-22 07:34:00 -0500624 if (l_propertyName.compare("CollectionStatus") ==
625 constants::STR_CMP_SUCCESS)
626 {
627 l_propertyMap.emplace(
628 l_propertyName,
629 constants::vpdCollectionNotStarted);
630 }
631 else
632 {
633 l_propertyMap.emplace(l_propertyName,
634 std::string{});
635 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500636 }
637 else if (std::holds_alternative<bool>(l_propertyValue))
638 {
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500639 if (l_propertyName.compare("Present") ==
640 constants::STR_CMP_SUCCESS)
641 {
642 l_propertyMap.emplace(l_propertyName, false);
643 }
Rekha Aparna4f722062025-05-02 03:39:32 -0500644 else if (l_propertyName.compare("Functional") ==
645 constants::STR_CMP_SUCCESS)
646 {
647 // Since FRU is not present functional property
648 // is considered as true.
649 l_propertyMap.emplace(l_propertyName, true);
650 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500651 }
652 }
653 io_interfaceMap.emplace(l_interface,
654 std::move(l_propertyMap));
655 }
656 }
657 }
658 }
659 catch (const std::exception& l_ex)
660 {
661 logging::logMessage("Failed to remove VPD for FRU: " + i_objectPath +
662 " with error: " + std::string(l_ex.what()));
663 }
664}
Sunny Srivastava78c91072025-02-05 14:09:50 +0530665
666/**
667 * @brief API to detect pass1 planar type.
668 *
669 * Based on HW version and IM keyword, This API detects is it is a pass1 planar
670 * or not.
671 *
672 * @return True if pass 1 planar, false otherwise.
673 */
Souvik Roy094a7352025-02-20 01:31:45 -0600674inline bool isPass1Planar() noexcept
Sunny Srivastava78c91072025-02-05 14:09:50 +0530675{
Souvik Roy094a7352025-02-20 01:31:45 -0600676 bool l_rc{false};
677 try
Sunny Srivastava78c91072025-02-05 14:09:50 +0530678 {
Souvik Roy094a7352025-02-20 01:31:45 -0600679 auto l_retVal = dbusUtility::readDbusProperty(
680 constants::pimServiceName, constants::systemVpdInvPath,
681 constants::viniInf, constants::kwdHW);
Sunny Srivastava78c91072025-02-05 14:09:50 +0530682
Souvik Roy094a7352025-02-20 01:31:45 -0600683 auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal);
684
685 l_retVal = dbusUtility::readDbusProperty(
686 constants::pimServiceName, constants::systemInvPath,
687 constants::vsbpInf, constants::kwdIM);
688
689 auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal);
690
691 if (l_hwVer && l_imValue)
Sunny Srivastava78c91072025-02-05 14:09:50 +0530692 {
Souvik Roy094a7352025-02-20 01:31:45 -0600693 if (l_hwVer->size() != constants::VALUE_2)
Sunny Srivastava78c91072025-02-05 14:09:50 +0530694 {
Souvik Roy094a7352025-02-20 01:31:45 -0600695 throw std::runtime_error("Invalid HW keyword length.");
696 }
697
698 if (l_imValue->size() != constants::VALUE_4)
699 {
700 throw std::runtime_error("Invalid IM keyword length.");
701 }
702
703 const types::BinaryVector l_everest{80, 00, 48, 00};
704 const types::BinaryVector l_fuji{96, 00, 32, 00};
705
706 if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji))
707 {
708 if ((*l_hwVer).at(1) < constants::VALUE_21)
709 {
710 l_rc = true;
711 }
712 }
713 else if ((*l_hwVer).at(1) < constants::VALUE_2)
714 {
715 l_rc = true;
Sunny Srivastava78c91072025-02-05 14:09:50 +0530716 }
717 }
Souvik Roy094a7352025-02-20 01:31:45 -0600718 }
719 catch (const std::exception& l_ex)
720 {
721 logging::logMessage("Failed to check for pass 1 planar. Error: " +
722 std::string(l_ex.what()));
Sunny Srivastava78c91072025-02-05 14:09:50 +0530723 }
724
Souvik Roy094a7352025-02-20 01:31:45 -0600725 return l_rc;
Sunny Srivastava78c91072025-02-05 14:09:50 +0530726}
Sunny Srivastavac6ef42d2025-02-19 19:17:10 +0530727
728/**
729 * @brief API to detect if system configuration is that of PowerVS system.
730 *
731 * @param[in] i_imValue - IM value of the system.
732 * @return true if it is PowerVS configuration, false otherwise.
733 */
734inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue)
735{
736 if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
737 {
738 return false;
739 }
740
741 // Should be a 0x5000XX series system.
742 if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
743 i_imValue.at(1) == constants::HEX_VALUE_00)
744 {
745 std::string l_imagePrefix = dbusUtility::getImagePrefix();
746
747 // Check image for 0x500030XX series.
748 if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
749 ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
750 (l_imagePrefix == constants::powerVsImagePrefix_NY)))
751 {
752 logging::logMessage("PowerVS configuration");
753 return true;
754 }
755
756 // Check image for 0X500010XX series.
757 if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
758 ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
759 (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
760 {
761 logging::logMessage("PowerVS configuration");
762 return true;
763 }
764 }
765 return false;
766}
Sunny Srivastavaf1dda762025-02-19 23:46:19 +0530767
768/**
769 * @brief API to get CCIN for a given FRU from DBus.
770 *
771 * The API reads the CCIN for a FRU based on its inventory path.
772 *
773 * @param[in] i_invObjPath - Inventory path of the FRU.
774 * @return CCIN of the FRU on success, empty string otherwise.
775 */
776inline std::string getCcinFromDbus(const std::string& i_invObjPath)
777{
778 try
779 {
780 if (i_invObjPath.empty())
781 {
782 throw std::runtime_error("Empty EEPROM path, can't read CCIN");
783 }
784
785 const auto& l_retValue = dbusUtility::readDbusProperty(
786 constants::pimServiceName, i_invObjPath, constants::viniInf,
787 constants::kwdCCIN);
788
789 auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
790 if (!l_ptrCcin || (*l_ptrCcin).size() != constants::VALUE_4)
791 {
792 throw DbusException("Invalid CCIN read from Dbus");
793 }
794
795 return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
796 }
797 catch (const std::exception& l_ex)
798 {
799 logging::logMessage(l_ex.what());
800 return std::string{};
801 }
802}
Sunny Srivastavaecfaa212025-03-24 13:02:13 +0530803
804/**
805 * @brief API to check if the current running image is a powerVS image.
806 *
807 * @return true if it is PowerVS image, false otherwise.
808 */
809inline bool isPowerVsImage()
810{
811 std::string l_imagePrefix = dbusUtility::getImagePrefix();
812
813 if ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
814 (l_imagePrefix == constants::powerVsImagePrefix_NY) ||
815 (l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
816 (l_imagePrefix == constants::powerVsImagePrefix_NZ))
817 {
818 return true;
819 }
820 return false;
821}
Souvik Roy2ee8a212025-04-24 02:37:59 -0500822
823/**
824 * @brief API to sync keyword update to inherited FRUs.
825 *
826 * For a given keyword update on a EEPROM path, this API syncs the keyword
827 * update to all inherited FRUs' respective interface, property on PIM.
828 *
829 * @param[in] i_fruPath - EEPROM path of FRU.
830 * @param[in] i_paramsToWriteData - Input details.
831 * @param[in] i_sysCfgJsonObj - System config JSON.
832 *
833 */
834inline void updateKwdOnInheritedFrus(
835 const std::string& i_fruPath,
836 const types::WriteVpdParams& i_paramsToWriteData,
837 const nlohmann::json& i_sysCfgJsonObj) noexcept
838{
839 try
840 {
841 if (!i_sysCfgJsonObj.contains("frus"))
842 {
843 throw std::runtime_error("Mandatory tag(s) missing from JSON");
844 }
845
846 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
847 {
848 throw std::runtime_error(
849 "VPD path [" + i_fruPath + "] not found in system config JSON");
850 }
851
852 const types::IpzData* l_ipzData =
853 std::get_if<types::IpzData>(&i_paramsToWriteData);
854
855 if (!l_ipzData)
856 {
857 throw std::runtime_error("Unsupported VPD type");
858 }
859 // iterate through all inventory paths for given EEPROM path,
860 // except the base FRU.
861 // if for an inventory path, "inherit" tag is true,
862 // update the inventory path's com.ibm.ipzvpd.<record>,keyword
863 // property
864
865 types::ObjectMap l_objectInterfaceMap;
866
867 auto l_populateInterfaceMap =
868 [&l_objectInterfaceMap,
869 &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) {
870 // update inherited FRUs only
871 if (l_Fru.value("inherit", true))
872 {
873 l_objectInterfaceMap.emplace(
874 sdbusplus::message::object_path{l_Fru["inventoryPath"]},
875 types::InterfaceMap{
876 {std::string{constants::ipzVpdInf +
877 std::get<0>(*l_ipzData)},
878 types::PropertyMap{{std::get<1>(*l_ipzData),
879 std::get<2>(*l_ipzData)}}}});
880 }
881 };
882
883 // iterate through all FRUs except the base FRU
884 std::for_each(
885 i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1,
886 i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap);
887
888 if (!l_objectInterfaceMap.empty())
889 {
890 // notify PIM
891 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
892 {
893 throw std::runtime_error(
894 "Call to PIM failed for VPD file " + i_fruPath);
895 }
896 }
897 }
898 catch (const std::exception& l_ex)
899 {
900 logging::logMessage(
901 "Failed to sync keyword update to inherited FRUs of FRU [" +
902 i_fruPath + "]. Error: " + std::string(l_ex.what()));
903 }
904}
Souvik Roy96ebe962025-04-29 04:01:07 -0500905
906/**
907 * @brief API to get common interface(s) properties corresponding to given
908 * record and keyword.
909 *
910 * For a given record and keyword, this API finds the corresponding common
911 * interfaces(s) properties from the system config JSON and populates an
912 * interface map with the respective properties and values.
913 *
914 * @param[in] i_paramsToWriteData - Input details.
915 * @param[in] i_commonInterfaceJson - Common interface JSON object.
916 *
917 * @return Returns a map of common interface(s) and properties corresponding to
918 * the record and keyword. An empty map is returned if no such common
919 * interface(s) and properties are found.
920 */
921inline types::InterfaceMap getCommonInterfaceProperties(
922 const types::WriteVpdParams& i_paramsToWriteData,
923 const nlohmann::json& i_commonInterfaceJson) noexcept
924{
925 types::InterfaceMap l_interfaceMap;
926 try
927 {
928 const types::IpzData* l_ipzData =
929 std::get_if<types::IpzData>(&i_paramsToWriteData);
930
931 if (!l_ipzData)
932 {
933 throw std::runtime_error("Invalid VPD type");
934 }
935
936 auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData),
937 &l_interfaceMap](
938 const auto& l_interfacesPropPair) {
939 // find matching property value pair
940 const auto l_matchPropValuePairIt = std::find_if(
941 l_interfacesPropPair.value().items().begin(),
942 l_interfacesPropPair.value().items().end(),
943 [&l_ipzData](const auto& l_propValuePair) {
944 return (l_propValuePair.value().value("recordName", "") ==
945 std::get<0>(*l_ipzData) &&
946 l_propValuePair.value().value("keywordName", "") ==
947 std::get<1>(*l_ipzData));
948 });
949
950 if (l_matchPropValuePairIt !=
951 l_interfacesPropPair.value().items().end())
952 {
953 // add property map to interface map
954 l_interfaceMap.emplace(
955 l_interfacesPropPair.key(),
956 types::PropertyMap{
957 {l_matchPropValuePairIt.key(),
958 vpdSpecificUtility::encodeKeyword(
959 std::string(std::get<2>(*l_ipzData).begin(),
960 std::get<2>(*l_ipzData).end()),
961 l_matchPropValuePairIt.value().value("encoding",
962 ""))}});
963 }
964 };
965
966 // iterate through all common interfaces and populate interface map
967 std::for_each(i_commonInterfaceJson.items().begin(),
968 i_commonInterfaceJson.items().end(),
969 l_populateInterfaceMap);
970 }
971 catch (const std::exception& l_ex)
972 {
973 logging::logMessage(
974 "Failed to find common interface properties. Error: " +
975 std::string(l_ex.what()));
976 }
977 return l_interfaceMap;
978}
979
980/**
981 * @brief API to update common interface(s) properties when keyword is updated.
982 *
983 * For a given keyword update on a EEPROM path, this API syncs the keyword
984 * update to respective common interface(s) properties of the base FRU and all
985 * inherited FRUs.
986 *
987 * @param[in] i_fruPath - EEPROM path of FRU.
988 * @param[in] i_paramsToWriteData - Input details.
989 * @param[in] i_sysCfgJsonObj - System config JSON.
990 *
991 */
992inline void updateCiPropertyOfInheritedFrus(
993 const std::string& i_fruPath,
994 const types::WriteVpdParams& i_paramsToWriteData,
995 const nlohmann::json& i_sysCfgJsonObj) noexcept
996{
997 try
998 {
999 if (!i_sysCfgJsonObj.contains("commonInterfaces"))
1000 {
1001 // no common interfaces in JSON, nothing to do
1002 return;
1003 }
1004
1005 if (!i_sysCfgJsonObj.contains("frus"))
1006 {
1007 throw std::runtime_error("Mandatory tag(s) missing from JSON");
1008 }
1009
1010 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1011 {
1012 throw std::runtime_error(
1013 "VPD path [" + i_fruPath + "] not found in system config JSON");
1014 }
1015
1016 if (!std::get_if<types::IpzData>(&i_paramsToWriteData))
1017 {
1018 throw std::runtime_error("Unsupported VPD type");
1019 }
1020
1021 // iterate through all inventory paths for given EEPROM path,
1022 // if for an inventory path, "inherit" tag is true,
1023 // update the inventory path's com.ibm.ipzvpd.<record>,keyword
1024 // property
1025
1026 types::ObjectMap l_objectInterfaceMap;
1027
1028 const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties(
1029 i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"]);
1030
1031 if (l_interfaceMap.empty())
1032 {
1033 // nothing to do
1034 return;
1035 }
1036
1037 auto l_populateObjectInterfaceMap =
1038 [&l_objectInterfaceMap, &l_interfaceMap = std::as_const(
1039 l_interfaceMap)](const auto& l_Fru) {
1040 if (l_Fru.value("inherit", true) &&
1041 l_Fru.contains("inventoryPath"))
1042 {
1043 l_objectInterfaceMap.emplace(
1044 sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1045 l_interfaceMap);
1046 }
1047 };
1048
1049 std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(),
1050 i_sysCfgJsonObj["frus"][i_fruPath].end(),
1051 l_populateObjectInterfaceMap);
1052
1053 if (!l_objectInterfaceMap.empty())
1054 {
1055 // notify PIM
1056 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1057 {
1058 throw std::runtime_error(
1059 "Call to PIM failed for VPD file " + i_fruPath);
1060 }
1061 }
1062 }
1063 catch (const std::exception& l_ex)
1064 {
1065 logging::logMessage(
1066 "Failed to update common interface properties of FRU [" +
1067 i_fruPath + "]. Error: " + std::string(l_ex.what()));
1068 }
1069}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001070} // namespace vpdSpecificUtility
1071} // namespace vpd