blob: 6500358f44541dac05121d33df8301a78d081ab9 [file] [log] [blame]
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301#include "config.h"
2
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05003#include "common_utility.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05304#include "defines.hpp"
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05005#include "editor_impl.hpp"
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05006#include "ibm_vpd_utils.hpp"
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05007#include "ipz_parser.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05308#include "keyword_vpd_parser.hpp"
Alpana Kumaria00936f2020-04-14 07:15:46 -05009#include "memory_vpd_parser.hpp"
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050010#include "parser_factory.hpp"
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -050011#include "vpd_exceptions.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053012
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050013#include <assert.h>
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -050014#include <ctype.h>
15
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053016#include <CLI/CLI.hpp>
alpana077ce68722021-07-25 13:23:59 -050017#include <boost/algorithm/string.hpp>
Patrick Williamsc78d8872023-05-10 07:50:56 -050018#include <gpiod.hpp>
19#include <phosphor-logging/log.hpp>
20
21#include <algorithm>
Alpana Kumari65b83602020-09-01 00:24:56 -050022#include <cstdarg>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053023#include <exception>
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053024#include <filesystem>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053025#include <fstream>
26#include <iostream>
27#include <iterator>
alpana077ce68722021-07-25 13:23:59 -050028#include <regex>
Santosh Puranik253fbe92022-10-06 22:38:09 +053029#include <thread>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053030
31using namespace std;
32using namespace openpower::vpd;
33using namespace CLI;
34using namespace vpd::keyword::parser;
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053035using namespace openpower::vpd::constants;
36namespace fs = filesystem;
37using json = nlohmann::json;
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050038using namespace openpower::vpd::parser::factory;
SunnySrivastava1984945a02d2020-05-06 01:55:41 -050039using namespace openpower::vpd::inventory;
Alpana Kumaria00936f2020-04-14 07:15:46 -050040using namespace openpower::vpd::memory::parser;
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050041using namespace openpower::vpd::parser::interface;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -050042using namespace openpower::vpd::exceptions;
Andrew Geissler280197e2020-12-08 20:51:49 -060043using namespace phosphor::logging;
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -050044using namespace openpower::vpd::manager::editor;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053045
Santosh Puranik88edeb62020-03-02 12:00:09 +053046/**
Sunny Srivastava37992a62023-07-11 05:18:41 -050047 * @brief API declaration, Populate Dbus.
48 *
49 * This method invokes all the populateInterface functions
50 * and notifies PIM about dbus object.
51 *
52 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
53 * input.
54 * @param[in] js - Inventory json object
55 * @param[in] filePath - Path of the vpd file
56 * @param[in] preIntrStr - Interface string
57 */
58template <typename T>
59static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath);
60
61/**
Santosh Puranike9c57532022-03-15 16:51:51 +053062 * @brief Returns the BMC state
63 */
64static auto getBMCState()
65{
66 std::string bmcState;
67 try
68 {
69 auto bus = sdbusplus::bus::new_default();
70 auto properties = bus.new_method_call(
71 "xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0",
72 "org.freedesktop.DBus.Properties", "Get");
73 properties.append("xyz.openbmc_project.State.BMC");
74 properties.append("CurrentBMCState");
75 auto result = bus.call(properties);
76 std::variant<std::string> val;
77 result.read(val);
78 if (auto pVal = std::get_if<std::string>(&val))
79 {
80 bmcState = *pVal;
81 }
82 }
83 catch (const sdbusplus::exception::SdBusError& e)
84 {
85 // Ignore any error
86 std::cerr << "Failed to get BMC state: " << e.what() << "\n";
87 }
88 return bmcState;
89}
90
91/**
92 * @brief Check if the FRU is in the cache
93 *
94 * Checks if the FRU associated with the supplied D-Bus object path is already
95 * on D-Bus. This can be used to test if a VPD collection is required for this
96 * FRU. It uses the "xyz.openbmc_project.Inventory.Item, Present" property to
97 * determine the presence of a FRU in the cache.
98 *
99 * @param objectPath - The D-Bus object path without the PIM prefix.
100 * @return true if the object exists on D-Bus, false otherwise.
101 */
102static auto isFruInVpdCache(const std::string& objectPath)
103{
104 try
105 {
106 auto bus = sdbusplus::bus::new_default();
107 auto invPath = std::string{pimPath} + objectPath;
108 auto props = bus.new_method_call(
109 "xyz.openbmc_project.Inventory.Manager", invPath.c_str(),
110 "org.freedesktop.DBus.Properties", "Get");
111 props.append("xyz.openbmc_project.Inventory.Item");
112 props.append("Present");
113 auto result = bus.call(props);
114 std::variant<bool> present;
115 result.read(present);
116 if (auto pVal = std::get_if<bool>(&present))
117 {
118 return *pVal;
119 }
120 return false;
121 }
122 catch (const sdbusplus::exception::SdBusError& e)
123 {
124 std::cout << "FRU: " << objectPath << " not in D-Bus\n";
125 // Assume not present in case of an error
126 return false;
127 }
128}
129
130/**
131 * @brief Check if VPD recollection is needed for the given EEPROM
132 *
133 * Not all FRUs can be swapped at BMC ready state. This function does the
134 * following:
135 * -- Check if the FRU is marked as "pluggableAtStandby" OR
136 * "concurrentlyMaintainable". If so, return true.
137 * -- Check if we are at BMC NotReady state. If we are, then return true.
138 * -- Else check if the FRU is not present in the VPD cache (to cover for VPD
139 * force collection). If not found in the cache, return true.
140 * -- Else return false.
141 *
142 * @param js - JSON Object.
143 * @param filePath - The EEPROM file.
144 * @return true if collection should be attempted, false otherwise.
145 */
146static auto needsRecollection(const nlohmann::json& js, const string& filePath)
147{
148 if (js["frus"][filePath].at(0).value("pluggableAtStandby", false) ||
149 js["frus"][filePath].at(0).value("concurrentlyMaintainable", false))
150 {
151 return true;
152 }
153 if (getBMCState() == "xyz.openbmc_project.State.BMC.BMCState.NotReady")
154 {
155 return true;
156 }
157 if (!isFruInVpdCache(js["frus"][filePath].at(0).value("inventoryPath", "")))
158 {
159 return true;
160 }
161 return false;
162}
163
164/**
Santosh Puranik88edeb62020-03-02 12:00:09 +0530165 * @brief Expands location codes
166 */
167static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
168 bool isSystemVpd)
169{
170 auto expanded{unexpanded};
171 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
172 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
173 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
174 size_t idx = expanded.find("fcs");
175 try
176 {
177 if (idx != string::npos)
178 {
179 string fc{};
180 string se{};
181 if (isSystemVpd)
182 {
183 const auto& fcData = vpdMap.at("VCEN").at("FC");
184 const auto& seData = vpdMap.at("VCEN").at("SE");
185 fc = string(fcData.data(), fcData.size());
186 se = string(seData.data(), seData.size());
187 }
188 else
189 {
190 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
191 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
192 }
193
Alpana Kumari81671f62021-02-10 02:21:59 -0600194 // TODO: See if ND0 can be placed in the JSON
195 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND0." + se);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530196 }
197 else
198 {
199 idx = expanded.find("mts");
200 if (idx != string::npos)
201 {
202 string mt{};
203 string se{};
204 if (isSystemVpd)
205 {
206 const auto& mtData = vpdMap.at("VSYS").at("TM");
207 const auto& seData = vpdMap.at("VSYS").at("SE");
208 mt = string(mtData.data(), mtData.size());
209 se = string(seData.data(), seData.size());
210 }
211 else
212 {
213 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
214 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
215 }
216
217 replace(mt.begin(), mt.end(), '-', '.');
218 expanded.replace(idx, 3, mt + "." + se);
219 }
220 }
221 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500222 catch (const exception& e)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530223 {
jinuthomasf457a3e2023-04-13 12:22:48 -0500224 std::cerr << "Failed to expand location code with exception: "
225 << e.what() << "\n";
Santosh Puranik88edeb62020-03-02 12:00:09 +0530226 }
227 return expanded;
228}
Alpana Kumari2f793042020-08-18 05:51:03 -0500229
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530230/**
231 * @brief Populate FRU specific interfaces.
232 *
233 * This is a common method which handles both
234 * ipz and keyword specific interfaces thus,
235 * reducing the code redundancy.
236 * @param[in] map - Reference to the innermost keyword-value map.
237 * @param[in] preIntrStr - Reference to the interface string.
238 * @param[out] interfaces - Reference to interface map.
239 */
240template <typename T>
241static void populateFruSpecificInterfaces(const T& map,
242 const string& preIntrStr,
243 inventory::InterfaceMap& interfaces)
244{
245 inventory::PropertyMap prop;
246
247 for (const auto& kwVal : map)
248 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530249 auto kw = kwVal.first;
250
251 if (kw[0] == '#')
252 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500253 kw = string("PD_") + kw[1];
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530254 }
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500255 else if (isdigit(kw[0]))
256 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500257 kw = string("N_") + kw;
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500258 }
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000259 if constexpr (is_same<T, KeywordVpdMap>::value)
260 {
jinuthomasd640f692023-03-28 04:13:23 -0500261 if (auto keywordValue = get_if<Binary>(&kwVal.second))
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000262 {
jinuthomasd640f692023-03-28 04:13:23 -0500263 Binary vec((*keywordValue).begin(), (*keywordValue).end());
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000264 prop.emplace(move(kw), move(vec));
265 }
jinuthomasd640f692023-03-28 04:13:23 -0500266 else if (auto keywordValue = get_if<std::string>(&kwVal.second))
267 {
268 Binary vec((*keywordValue).begin(), (*keywordValue).end());
269 prop.emplace(move(kw), move(vec));
270 }
271 else if (auto keywordValue = get_if<size_t>(&kwVal.second))
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000272 {
273 if (kw == "MemorySizeInKB")
274 {
275 inventory::PropertyMap memProp;
jinuthomasd640f692023-03-28 04:13:23 -0500276 memProp.emplace(move(kw), ((*keywordValue)));
277 interfaces.emplace(
278 "xyz.openbmc_project.Inventory.Item.Dimm",
279 move(memProp));
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000280 }
jinuthomasd640f692023-03-28 04:13:23 -0500281 else
282 {
jinuthomasf457a3e2023-04-13 12:22:48 -0500283 std::cerr << "Unknown Keyword[" << kw << "] found ";
jinuthomasd640f692023-03-28 04:13:23 -0500284 }
285 }
286 else
287 {
jinuthomasf457a3e2023-04-13 12:22:48 -0500288 std::cerr << "Unknown Variant found ";
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000289 }
290 }
291 else
292 {
293 Binary vec(kwVal.second.begin(), kwVal.second.end());
294 prop.emplace(move(kw), move(vec));
295 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530296 }
297
298 interfaces.emplace(preIntrStr, move(prop));
299}
300
301/**
302 * @brief Populate Interfaces.
303 *
304 * This method populates common and extra interfaces to dbus.
305 * @param[in] js - json object
306 * @param[out] interfaces - Reference to interface map
307 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530308 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530309 */
310template <typename T>
311static void populateInterfaces(const nlohmann::json& js,
312 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530313 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530314{
315 for (const auto& ifs : js.items())
316 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530317 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530318 inventory::PropertyMap props;
319
320 for (const auto& itr : ifs.value().items())
321 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530322 const string& busProp = itr.key();
323
Alpana Kumari31970de2020-02-17 06:49:57 -0600324 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530325 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530326 props.emplace(busProp, itr.value().get<bool>());
327 }
328 else if (itr.value().is_string())
329 {
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600330 if (busProp == "LocationCode" && inf == IBM_LOCATION_CODE_INF)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530331 {
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600332 std::string prop;
333 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530334 {
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000335 // TODO deprecate the com.ibm interface later
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600336 prop = expandLocationCode(itr.value().get<string>(),
337 vpdMap, isSystemVpd);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530338 }
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600339 else if constexpr (is_same<T, KeywordVpdMap>::value)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530340 {
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600341 // Send empty Parsed object to expandLocationCode api.
342 prop = expandLocationCode(itr.value().get<string>(),
343 Parsed{}, false);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530344 }
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600345 props.emplace(busProp, prop);
346 interfaces.emplace(XYZ_LOCATION_CODE_INF, props);
347 interfaces.emplace(IBM_LOCATION_CODE_INF, props);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530348 }
349 else
350 {
351 props.emplace(busProp, itr.value().get<string>());
352 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530353 }
Santosh Puraniked609af2021-06-21 11:30:07 +0530354 else if (itr.value().is_array())
355 {
356 try
357 {
358 props.emplace(busProp, itr.value().get<Binary>());
359 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500360 catch (const nlohmann::detail::type_error& e)
Santosh Puraniked609af2021-06-21 11:30:07 +0530361 {
362 std::cerr << "Type exception: " << e.what() << "\n";
363 // Ignore any type errors
364 }
365 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600366 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530367 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600368 const string& rec = itr.value().value("recordName", "");
369 const string& kw = itr.value().value("keywordName", "");
370 const string& encoding = itr.value().value("encoding", "");
371
Alpana Kumari58e22142020-05-05 00:22:12 -0500372 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530373 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530374 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
375 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600376 {
Patrick Williamsc78d8872023-05-10 07:50:56 -0500377 auto encoded = encodeKeyword(vpdMap.at(rec).at(kw),
378 encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530379 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600380 }
381 }
Alpana Kumari58e22142020-05-05 00:22:12 -0500382 else if constexpr (is_same<T, KeywordVpdMap>::value)
Alpana Kumari31970de2020-02-17 06:49:57 -0600383 {
384 if (!kw.empty() && vpdMap.count(kw))
385 {
jinuthomasd640f692023-03-28 04:13:23 -0500386 if (auto kwValue = get_if<Binary>(&vpdMap.at(kw)))
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000387 {
Patrick Williamsc78d8872023-05-10 07:50:56 -0500388 auto prop = string((*kwValue).begin(),
389 (*kwValue).end());
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000390
391 auto encoded = encodeKeyword(prop, encoding);
392
393 props.emplace(busProp, encoded);
394 }
jinuthomasd640f692023-03-28 04:13:23 -0500395 else if (auto kwValue =
396 get_if<std::string>(&vpdMap.at(kw)))
397 {
Patrick Williamsc78d8872023-05-10 07:50:56 -0500398 auto prop = string((*kwValue).begin(),
399 (*kwValue).end());
jinuthomasd640f692023-03-28 04:13:23 -0500400
401 auto encoded = encodeKeyword(prop, encoding);
402
403 props.emplace(busProp, encoded);
404 }
405 else if (auto uintValue =
406 get_if<size_t>(&vpdMap.at(kw)))
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000407 {
408 props.emplace(busProp, *uintValue);
409 }
jinuthomasd640f692023-03-28 04:13:23 -0500410 else
411 {
412 std::cerr << " Unknown Keyword [" << kw
413 << "] Encountered";
414 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600415 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530416 }
417 }
Matt Spinlerb1e64bb2021-09-08 09:57:48 -0500418 else if (itr.value().is_number())
419 {
420 // For now assume the value is a size_t. In the future it would
421 // be nice to come up with a way to get the type from the JSON.
422 props.emplace(busProp, itr.value().get<size_t>());
423 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530424 }
Priyanga Ramasamyaa8a8932022-01-27 09:12:41 -0600425 insertOrMerge(interfaces, inf, move(props));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530426 }
427}
428
alpana075cb3b1f2021-12-16 11:19:36 -0600429/**
430 * @brief This API checks if this FRU is pcie_devices. If yes then it further
431 * checks whether it is PASS1 planar.
432 */
433static bool isThisPcieOnPass1planar(const nlohmann::json& js,
434 const string& file)
435{
436 auto isThisPCIeDev = false;
437 auto isPASS1 = false;
438
439 // Check if it is a PCIE device
440 if (js["frus"].find(file) != js["frus"].end())
441 {
Santosh Puranikc03f3902022-04-14 10:58:26 +0530442 if ((js["frus"][file].at(0).find("extraInterfaces") !=
443 js["frus"][file].at(0).end()))
alpana075cb3b1f2021-12-16 11:19:36 -0600444 {
Santosh Puranikc03f3902022-04-14 10:58:26 +0530445 if (js["frus"][file].at(0)["extraInterfaces"].find(
alpana075cb3b1f2021-12-16 11:19:36 -0600446 "xyz.openbmc_project.Inventory.Item.PCIeDevice") !=
Santosh Puranikc03f3902022-04-14 10:58:26 +0530447 js["frus"][file].at(0)["extraInterfaces"].end())
alpana075cb3b1f2021-12-16 11:19:36 -0600448 {
449 isThisPCIeDev = true;
450 }
451 }
452 }
453
454 if (isThisPCIeDev)
455 {
Alpana Kumaria6181e22022-05-12 05:01:53 -0500456 // Collect HW version and SystemType to know if it is PASS1 planar.
alpana075cb3b1f2021-12-16 11:19:36 -0600457 auto bus = sdbusplus::bus::new_default();
Alpana Kumaria6181e22022-05-12 05:01:53 -0500458 auto property1 = bus.new_method_call(
alpana075cb3b1f2021-12-16 11:19:36 -0600459 INVENTORY_MANAGER_SERVICE,
460 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
461 "org.freedesktop.DBus.Properties", "Get");
Alpana Kumaria6181e22022-05-12 05:01:53 -0500462 property1.append("com.ibm.ipzvpd.VINI");
463 property1.append("HW");
464 auto result1 = bus.call(property1);
465 inventory::Value hwVal;
466 result1.read(hwVal);
alpana075cb3b1f2021-12-16 11:19:36 -0600467
Alpana Kumaria6181e22022-05-12 05:01:53 -0500468 // SystemType
469 auto property2 = bus.new_method_call(
470 INVENTORY_MANAGER_SERVICE,
471 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
472 "org.freedesktop.DBus.Properties", "Get");
473 property2.append("com.ibm.ipzvpd.VSBP");
474 property2.append("IM");
475 auto result2 = bus.call(property2);
476 inventory::Value imVal;
477 result2.read(imVal);
478
479 auto pVal1 = get_if<Binary>(&hwVal);
480 auto pVal2 = get_if<Binary>(&imVal);
481
482 if (pVal1 && pVal2)
alpana075cb3b1f2021-12-16 11:19:36 -0600483 {
Alpana Kumaria6181e22022-05-12 05:01:53 -0500484 auto hwVersion = *pVal1;
485 auto systemType = *pVal2;
486
487 // IM kw for Everest
488 Binary everestSystem{80, 00, 48, 00};
489
490 if (systemType == everestSystem)
491 {
492 if (hwVersion[1] < 21)
493 {
494 isPASS1 = true;
495 }
496 }
497 else if (hwVersion[1] < 2)
498 {
alpana075cb3b1f2021-12-16 11:19:36 -0600499 isPASS1 = true;
Alpana Kumaria6181e22022-05-12 05:01:53 -0500500 }
alpana075cb3b1f2021-12-16 11:19:36 -0600501 }
502 }
503
504 return (isThisPCIeDev && isPASS1);
505}
506
Alpana Kumari735dee92022-03-25 01:24:40 -0500507/** Performs any pre-action needed to get the FRU setup for collection.
Alpana Kumari2f793042020-08-18 05:51:03 -0500508 *
509 * @param[in] json - json object
510 * @param[in] file - eeprom file path
511 */
512static void preAction(const nlohmann::json& json, const string& file)
513{
Alpana Kumari735dee92022-03-25 01:24:40 -0500514 if ((json["frus"][file].at(0)).find("preAction") ==
Alpana Kumari2f793042020-08-18 05:51:03 -0500515 json["frus"][file].at(0).end())
516 {
Alpana Kumari735dee92022-03-25 01:24:40 -0500517 return;
Alpana Kumari2f793042020-08-18 05:51:03 -0500518 }
519
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500520 try
Alpana Kumari2f793042020-08-18 05:51:03 -0500521 {
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500522 if (executePreAction(json, file))
Alpana Kumari2f793042020-08-18 05:51:03 -0500523 {
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500524 if (json["frus"][file].at(0).find("devAddress") !=
525 json["frus"][file].at(0).end())
Alpana Kumari40d1c192022-03-09 21:16:02 -0600526 {
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500527 // Now bind the device
528 string bind = json["frus"][file].at(0).value("devAddress", "");
jinuthomasf457a3e2023-04-13 12:22:48 -0500529 std::cout << "Binding device " << bind << std::endl;
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500530 string bindCmd = string("echo \"") + bind +
531 string("\" > /sys/bus/i2c/drivers/at24/bind");
jinuthomasf457a3e2023-04-13 12:22:48 -0500532 std::cout << bindCmd << std::endl;
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500533 executeCmd(bindCmd);
534
535 // Check if device showed up (test for file)
536 if (!fs::exists(file))
537 {
jinuthomasf457a3e2023-04-13 12:22:48 -0500538 std::cerr << "EEPROM " << file
539 << " does not exist. Take failure action"
540 << std::endl;
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500541 // If not, then take failure postAction
542 executePostFailAction(json, file);
543 }
544 }
545 else
546 {
547 // missing required informations
jinuthomasf457a3e2023-04-13 12:22:48 -0500548 std::cerr << "VPD inventory JSON missing basic informations of "
549 "preAction "
550 "for this FRU : ["
551 << file << "]. Executing executePostFailAction."
552 << std::endl;
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500553
554 // Take failure postAction
Alpana Kumari40d1c192022-03-09 21:16:02 -0600555 executePostFailAction(json, file);
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500556 return;
Alpana Kumari40d1c192022-03-09 21:16:02 -0600557 }
558 }
Santosh Puranikdedb5a62022-12-19 23:58:32 +0530559 else
560 {
561 // If the FRU is not there, clear the VINI/CCIN data.
562 // Enity manager probes for this keyword to look for this
563 // FRU, now if the data is persistent on BMC and FRU is
564 // removed this can lead to ambiguity. Hence clearing this
565 // Keyword if FRU is absent.
566 const auto& invPath =
567 json["frus"][file].at(0).value("inventoryPath", "");
568
569 if (!invPath.empty())
570 {
571 inventory::ObjectMap pimObjMap{
572 {invPath, {{"com.ibm.ipzvpd.VINI", {{"CC", Binary{}}}}}}};
573
574 common::utility::callPIM(move(pimObjMap));
575 }
576 else
577 {
578 throw std::runtime_error("Path empty in Json");
579 }
580 }
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500581 }
582 catch (const GpioException& e)
583 {
584 PelAdditionalData additionalData{};
585 additionalData.emplace("DESCRIPTION", e.what());
586 createPEL(additionalData, PelSeverity::WARNING, errIntfForGpioError,
587 nullptr);
Alpana Kumari2f793042020-08-18 05:51:03 -0500588 }
Alpana Kumari2f793042020-08-18 05:51:03 -0500589}
590
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530591/**
Santosh Puranikf3e69682022-03-31 17:52:38 +0530592 * @brief Fills the Decorator.AssetTag property into the interfaces map
593 *
594 * This function should only be called in cases where we did not find a JSON
595 * symlink. A missing symlink in /var/lib will be considered as a factory reset
596 * and this function will be used to default the AssetTag property.
597 *
598 * @param interfaces A possibly pre-populated map of inetrfaces to properties.
599 * @param vpdMap A VPD map of the system VPD data.
600 */
601static void fillAssetTag(inventory::InterfaceMap& interfaces,
602 const Parsed& vpdMap)
603{
604 // Read the system serial number and MTM
605 // Default asset tag is Server-MTM-System Serial
606 inventory::Interface assetIntf{
607 "xyz.openbmc_project.Inventory.Decorator.AssetTag"};
608 inventory::PropertyMap assetTagProps;
609 std::string defaultAssetTag =
610 std::string{"Server-"} + getKwVal(vpdMap, "VSYS", "TM") +
611 std::string{"-"} + getKwVal(vpdMap, "VSYS", "SE");
612 assetTagProps.emplace("AssetTag", defaultAssetTag);
613 insertOrMerge(interfaces, assetIntf, std::move(assetTagProps));
614}
615
616/**
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530617 * @brief Set certain one time properties in the inventory
618 * Use this function to insert the Functional and Enabled properties into the
619 * inventory map. This function first checks if the object in question already
620 * has these properties hosted on D-Bus, if the property is already there, it is
621 * not modified, hence the name "one time". If the property is not already
622 * present, it will be added to the map with a suitable default value (true for
Santosh Puranikdedb5a62022-12-19 23:58:32 +0530623 * Functional and Enabled)
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530624 *
625 * @param[in] object - The inventory D-Bus obejct without the inventory prefix.
626 * @param[inout] interfaces - Reference to a map of inventory interfaces to
627 * which the properties will be attached.
628 */
629static void setOneTimeProperties(const std::string& object,
630 inventory::InterfaceMap& interfaces)
631{
632 auto bus = sdbusplus::bus::new_default();
633 auto objectPath = INVENTORY_PATH + object;
634 auto prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
635 objectPath.c_str(),
636 "org.freedesktop.DBus.Properties", "Get");
637 prop.append("xyz.openbmc_project.State.Decorator.OperationalStatus");
638 prop.append("Functional");
639 try
640 {
641 auto result = bus.call(prop);
642 }
643 catch (const sdbusplus::exception::SdBusError& e)
644 {
645 // Treat as property unavailable
646 inventory::PropertyMap prop;
647 prop.emplace("Functional", true);
648 interfaces.emplace(
649 "xyz.openbmc_project.State.Decorator.OperationalStatus",
650 move(prop));
651 }
652 prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
653 objectPath.c_str(),
654 "org.freedesktop.DBus.Properties", "Get");
655 prop.append("xyz.openbmc_project.Object.Enable");
656 prop.append("Enabled");
657 try
658 {
659 auto result = bus.call(prop);
660 }
661 catch (const sdbusplus::exception::SdBusError& e)
662 {
663 // Treat as property unavailable
664 inventory::PropertyMap prop;
Santosh Puranikdedb5a62022-12-19 23:58:32 +0530665 prop.emplace("Enabled", true);
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530666 interfaces.emplace("xyz.openbmc_project.Object.Enable", move(prop));
667 }
668}
669
670/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530671 * @brief Prime the Inventory
672 * Prime the inventory by populating only the location code,
673 * type interface and the inventory object for the frus
674 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530675 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530676 * @param[in] jsObject - Reference to vpd inventory json object
677 * @param[in] vpdMap - Reference to the parsed vpd map
678 *
679 * @returns Map of items in extraInterface.
680 */
681template <typename T>
682inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
683 const T& vpdMap)
684{
685 inventory::ObjectMap objects;
686
687 for (auto& itemFRUS : jsObject["frus"].items())
688 {
689 for (auto& itemEEPROM : itemFRUS.value())
690 {
Alpana Kumari2e6c6f72020-12-03 00:10:03 -0600691 // Take pre actions if needed
692 if (itemEEPROM.find("preAction") != itemEEPROM.end())
693 {
694 preAction(jsObject, itemFRUS.key());
695 }
696
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530697 inventory::InterfaceMap interfaces;
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530698 inventory::Object object(itemEEPROM.at("inventoryPath"));
699
Santosh Puranik50f60bf2021-05-26 17:55:06 +0530700 if ((itemFRUS.key() != systemVpdFilePath) &&
701 !itemEEPROM.value("noprime", false))
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530702 {
Alpana Kumaricfd7a752021-02-07 23:23:01 -0600703 inventory::PropertyMap presProp;
Priyanga Ramasamye358acb2022-03-21 14:21:50 -0500704
705 // Do not populate Present property for frus whose
Priyanga Ramasamyaca61372023-01-24 08:02:28 -0600706 // synthesized=true. synthesized=true says the fru VPD is
707 // synthesized and owned by a separate component.
708 // In some cases, the FRU has its own VPD, but still a separate
709 // application handles the FRU's presence. So VPD parser skips
710 // populating Present property by checking the JSON flag,
711 // "handlePresence".
Priyanga Ramasamye358acb2022-03-21 14:21:50 -0500712 if (!itemEEPROM.value("synthesized", false))
713 {
Priyanga Ramasamyaca61372023-01-24 08:02:28 -0600714 if (itemEEPROM.value("handlePresence", true))
715 {
716 presProp.emplace("Present", false);
717 interfaces.emplace("xyz.openbmc_project.Inventory.Item",
718 presProp);
719 }
Priyanga Ramasamye358acb2022-03-21 14:21:50 -0500720 }
Priyanga Ramasamyaca61372023-01-24 08:02:28 -0600721
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530722 setOneTimeProperties(object, interfaces);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530723 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
724 {
725 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
726 {
727 inventory::PropertyMap props;
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000728 if (eI.key() == IBM_LOCATION_CODE_INF)
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530729 {
730 if constexpr (std::is_same<T, Parsed>::value)
731 {
732 for (auto& lC : eI.value().items())
733 {
734 auto propVal = expandLocationCode(
735 lC.value().get<string>(), vpdMap, true);
736
737 props.emplace(move(lC.key()),
738 move(propVal));
Santosh Puranikb0f37492021-06-21 09:42:47 +0530739 interfaces.emplace(XYZ_LOCATION_CODE_INF,
740 props);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530741 interfaces.emplace(move(eI.key()),
742 move(props));
743 }
744 }
745 }
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530746 else if (eI.key() ==
747 "xyz.openbmc_project.Inventory.Item")
748 {
749 for (auto& val : eI.value().items())
750 {
751 if (val.key() == "PrettyName")
752 {
753 presProp.emplace(val.key(),
754 val.value().get<string>());
755 }
756 }
757 // Use insert_or_assign here as we may already have
758 // inserted the present property only earlier in
759 // this function under this same interface.
760 interfaces.insert_or_assign(eI.key(),
761 move(presProp));
762 }
Sunny Srivastava1bb87162024-02-13 00:09:09 -0600763 else
764 {
765 interfaces.emplace(move(eI.key()), move(props));
766 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530767 }
768 }
769 objects.emplace(move(object), move(interfaces));
770 }
771 }
772 }
773 return objects;
774}
775
Alpana Kumari65b83602020-09-01 00:24:56 -0500776/**
777 * @brief This API executes command to set environment variable
778 * And then reboot the system
779 * @param[in] key -env key to set new value
780 * @param[in] value -value to set.
781 */
782void setEnvAndReboot(const string& key, const string& value)
783{
784 // set env and reboot and break.
785 executeCmd("/sbin/fw_setenv", key, value);
Andrew Geissler280197e2020-12-08 20:51:49 -0600786 log<level::INFO>("Rebooting BMC to pick up new device tree");
Alpana Kumari65b83602020-09-01 00:24:56 -0500787 // make dbus call to reboot
788 auto bus = sdbusplus::bus::new_default_system();
789 auto method = bus.new_method_call(
790 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
791 "org.freedesktop.systemd1.Manager", "Reboot");
792 bus.call_noreply(method);
793}
794
795/*
796 * @brief This API checks for env var fitconfig.
797 * If not initialised OR updated as per the current system type,
798 * update this env var and reboot the system.
799 *
800 * @param[in] systemType IM kwd in vpd tells about which system type it is.
801 * */
802void setDevTreeEnv(const string& systemType)
803{
Alpana Kumari37e72702021-11-18 11:18:04 -0600804 // Init with default dtb
805 string newDeviceTree = "conf-aspeed-bmc-ibm-rainier-p1.dtb";
Santosh Puranike5f177a2022-01-24 20:14:46 +0530806 static const deviceTreeMap deviceTreeSystemTypeMap = {
807 {RAINIER_2U, "conf-aspeed-bmc-ibm-rainier-p1.dtb"},
808 {RAINIER_2U_V2, "conf-aspeed-bmc-ibm-rainier.dtb"},
809 {RAINIER_4U, "conf-aspeed-bmc-ibm-rainier-4u-p1.dtb"},
810 {RAINIER_4U_V2, "conf-aspeed-bmc-ibm-rainier-4u.dtb"},
811 {RAINIER_1S4U, "conf-aspeed-bmc-ibm-rainier-1s4u.dtb"},
Alpana Kumari1b026112022-03-02 23:41:38 -0600812 {EVEREST, "conf-aspeed-bmc-ibm-everest.dtb"},
Santosh Puranikdedb5a62022-12-19 23:58:32 +0530813 {EVEREST_V2, "conf-aspeed-bmc-ibm-everest.dtb"},
814 {BONNELL, "conf-aspeed-bmc-ibm-bonnell.dtb"}};
Alpana Kumari65b83602020-09-01 00:24:56 -0500815
816 if (deviceTreeSystemTypeMap.find(systemType) !=
817 deviceTreeSystemTypeMap.end())
818 {
819 newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
820 }
Alpana Kumari37e72702021-11-18 11:18:04 -0600821 else
822 {
823 // System type not supported
Alpana Kumariab1e22c2021-11-24 11:03:38 -0600824 string err = "This System type not found/supported in dtb table " +
825 systemType +
826 ".Please check the HW and IM keywords in the system "
827 "VPD.Breaking...";
828
829 // map to hold additional data in case of logging pel
830 PelAdditionalData additionalData{};
831 additionalData.emplace("DESCRIPTION", err);
832 createPEL(additionalData, PelSeverity::WARNING,
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500833 errIntfForInvalidSystemType, nullptr);
Alpana Kumariab1e22c2021-11-24 11:03:38 -0600834 exit(-1);
Alpana Kumari37e72702021-11-18 11:18:04 -0600835 }
Alpana Kumari65b83602020-09-01 00:24:56 -0500836
837 string readVarValue;
838 bool envVarFound = false;
839
840 vector<string> output = executeCmd("/sbin/fw_printenv");
841 for (const auto& entry : output)
842 {
843 size_t pos = entry.find("=");
844 string key = entry.substr(0, pos);
845 if (key != "fitconfig")
846 {
847 continue;
848 }
849
850 envVarFound = true;
851 if (pos + 1 < entry.size())
852 {
853 readVarValue = entry.substr(pos + 1);
854 if (readVarValue.find(newDeviceTree) != string::npos)
855 {
856 // fitconfig is Updated. No action needed
857 break;
858 }
859 }
860 // set env and reboot and break.
861 setEnvAndReboot(key, newDeviceTree);
862 exit(0);
863 }
864
865 // check If env var Not found
866 if (!envVarFound)
867 {
868 setEnvAndReboot("fitconfig", newDeviceTree);
869 }
870}
871
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530872/**
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500873 * @brief Parse the given EEPROM file.
874 *
875 * @param[in] vpdFilePath - Path of EEPROM file
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500876 * @param[in] js- Reference to vpd inventory json object
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500877 * @return Parsed VPD map
878 */
879std::variant<KeywordVpdMap, openpower::vpd::Store>
Sunny Srivastava37992a62023-07-11 05:18:41 -0500880 parseVpdFile(const std::string& vpdFilePath, const nlohmann::json& js)
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500881{
882 uint32_t vpdStartOffset = 0;
883 for (const auto& item : js["frus"][vpdFilePath])
884 {
885 if (item.find("offset") != item.end())
886 {
887 vpdStartOffset = item["offset"];
Sunny Srivastava37992a62023-07-11 05:18:41 -0500888 break;
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500889 }
890 }
891
892 Binary vpdVector = getVpdDataInVector(js, vpdFilePath);
893
894 ParserInterface* parser = ParserFactory::getParser(
Sunny Srivastava37992a62023-07-11 05:18:41 -0500895 vpdVector,
896 (pimPath + js["frus"][vpdFilePath][0]["inventoryPath"]
897 .get_ref<const nlohmann::json::string_t&>()),
898 vpdFilePath, vpdStartOffset);
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500899
900 auto parseResult = parser->parse();
901
902 // release the parser object
903 ParserFactory::freeParser(parser);
904
905 return parseResult;
906}
907
908/*
909 * @brief This API retrieves the hardware backup in map
910 *
911 * @param[in] systemVpdBackupPath - The path that backs up the system VPD.
912 * @param[in] backupVpdInvPath - FRU inventory path.
913 * @param[in] js - JSON object.
914 * @param[out] backupVpdMap - An IPZ VPD map containing the parsed backup VPD.
915 *
916 * */
917void getBackupVpdInMap(const string& systemVpdBackupPath,
918 const string& backupVpdInvPath, const nlohmann::json& js,
919 Parsed& backupVpdMap)
920{
921 PelAdditionalData additionalData{};
922
923 if (!fs::exists(systemVpdBackupPath))
924 {
925 string errorMsg = "Device path ";
926 errorMsg += systemVpdBackupPath;
927 errorMsg += " does not exist";
928
929 additionalData.emplace("DESCRIPTION", errorMsg);
930
931 additionalData.emplace("CALLOUT_INVENTORY_PATH",
932 INVENTORY_PATH + backupVpdInvPath);
933
934 createPEL(additionalData, PelSeverity::ERROR, errIntfForStreamFail,
935 nullptr);
936 }
937 else
938 {
Sunny Srivastava37992a62023-07-11 05:18:41 -0500939 auto backupVpdParsedResult = parseVpdFile(systemVpdBackupPath, js);
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500940
941 if (auto pVal = get_if<Store>(&backupVpdParsedResult))
942 {
943 backupVpdMap = pVal->getVpdMap();
944 }
Sunny Srivastava37992a62023-07-11 05:18:41 -0500945 else
946 {
947 std::cerr << "Invalid format of VPD in back up. Restore aborted."
948 << std::endl;
949 }
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500950 }
951}
952
953void updateVpdDataOnHw(const std::string& vpdFilePath, nlohmann::json& js,
954 const std::string& recName, const std::string& kwName,
955 const Binary& kwdData)
956{
957 const std::string& fruInvPath =
958 js["frus"][vpdFilePath][0]["inventoryPath"]
959 .get_ref<const nlohmann::json::string_t&>();
960
961 EditorImpl edit(vpdFilePath, js, recName, kwName, fruInvPath);
962
963 uint32_t offset = 0;
964 // Setup offset, if any
965 for (const auto& item : js["frus"][vpdFilePath])
966 {
967 if (item.find("offset") != item.end())
968 {
969 offset = item["offset"];
970 break;
971 }
972 }
973
974 // update keyword data on to EEPROM file
975 // Note: Updating keyword data on cache is
976 // handled via PIM Notify call hence passing
977 // the updCache flag value as false here.
978 edit.updateKeyword(kwdData, offset, false);
979}
980
981/**
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500982 * @brief API to check if we need to restore system VPD
983 * This functionality is only applicable for IPZ VPD data.
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500984
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500985 * @param[in] vpdMap - IPZ vpd map
986 * @param[in] objectPath - Object path for the FRU
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500987 * @param[in] js - JSON Object
988 * @param[in] isBackupOnCache - Denotes whether the backup is on cache/hardware
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500989 */
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500990void restoreSystemVPD(Parsed& vpdMap, const string& objectPath,
991 nlohmann::json& js, bool isBackupOnCache = true)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500992{
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -0500993 std::string systemVpdBackupPath{};
994 std::string backupVpdInvPath{};
995 Parsed backupVpdMap{};
996
997 if (!isBackupOnCache)
998 {
999 // Get the value of systemvpdBackupPath field from json
1000 systemVpdBackupPath = js["frus"][systemVpdFilePath].at(0).value(
1001 "systemVpdBackupPath", "");
1002
1003 backupVpdInvPath = js["frus"][systemVpdBackupPath][0]["inventoryPath"]
1004 .get_ref<const nlohmann::json::string_t&>();
1005
1006 getBackupVpdInMap(systemVpdBackupPath, backupVpdInvPath, js,
1007 backupVpdMap);
1008
1009 if (backupVpdMap.empty())
1010 {
Sunny Srivastava37992a62023-07-11 05:18:41 -05001011 std::cerr << "Backup VPD map is empty" << std::endl;
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001012 return;
1013 }
1014 }
1015
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001016 for (const auto& systemRecKwdPair : svpdKwdMap)
1017 {
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001018 const string& recordName = systemRecKwdPair.first;
1019 auto it = vpdMap.find(recordName);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001020
1021 // check if record is found in map we got by parser
1022 if (it != vpdMap.end())
1023 {
1024 const auto& kwdListForRecord = systemRecKwdPair.second;
Priyanga Ramasamy952d6c52022-11-07 07:20:24 -06001025 for (const auto& keywordInfo : kwdListForRecord)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001026 {
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001027 const auto keywordName = get<0>(keywordInfo);
Priyanga Ramasamy952d6c52022-11-07 07:20:24 -06001028
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001029 DbusPropertyMap& kwdValMap = it->second;
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001030 auto iterator = kwdValMap.find(keywordName);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001031
1032 if (iterator != kwdValMap.end())
1033 {
1034 string& kwdValue = iterator->second;
1035
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001036 std::string backupValue{};
Priyanga Ramasamy952d6c52022-11-07 07:20:24 -06001037 const auto& defaultValue = get<1>(keywordInfo);
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001038 const auto& backupVpdRecName = get<4>(keywordInfo);
1039 const auto& backupVpdKwName = get<5>(keywordInfo);
1040
1041 // If the 'isBackupOnCache' flag is false, we need
1042 // to backup the systemVPD on the specified fru's eeprom
1043 // path or restore it from the specified fru's eeprom path.
1044 if (isBackupOnCache)
1045 {
1046 // check bus data
1047 backupValue = readBusProperty(
1048 objectPath, ipzVpdInf + recordName, keywordName);
1049 }
1050 else
1051 {
Sunny Srivastava37992a62023-07-11 05:18:41 -05001052 backupValue = getKwVal(backupVpdMap, backupVpdRecName,
1053 backupVpdKwName);
1054
1055 if (backupValue.empty())
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001056 {
Sunny Srivastava37992a62023-07-11 05:18:41 -05001057 string errorMsg{};
1058 if (backupVpdMap.find(backupVpdRecName) ==
1059 backupVpdMap.end())
1060 {
1061 errorMsg = backupVpdRecName +
1062 " Record does not exist in "
1063 "the EEPROM file ";
1064 }
1065 else
1066 {
1067 errorMsg = backupVpdKwName +
1068 " Keyword not found or empty.";
1069 }
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001070
1071 errorMsg += systemVpdBackupPath;
1072
1073 PelAdditionalData additionalData;
1074 additionalData.emplace("DESCRIPTION", errorMsg);
1075
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001076 createPEL(additionalData, PelSeverity::ERROR,
1077 errIntfForInvalidVPD, nullptr);
1078
1079 continue;
1080 }
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001081 }
1082
1083 Binary backupDataInBinary(backupValue.begin(),
1084 backupValue.end());
1085
Priyanga Ramasamy952d6c52022-11-07 07:20:24 -06001086 Binary kwdDataInBinary(kwdValue.begin(), kwdValue.end());
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -05001087
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001088 if (backupDataInBinary != defaultValue)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001089 {
Priyanga Ramasamy952d6c52022-11-07 07:20:24 -06001090 if (kwdDataInBinary != defaultValue)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001091 {
1092 // both the data are present, check for mismatch
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001093 if (backupValue != kwdValue)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001094 {
Priyanga Ramasamy24942232023-01-05 04:54:59 -06001095 string errMsg = "Mismatch found between backup "
1096 "and primary VPD for record: ";
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001097 errMsg += (*it).first;
1098 errMsg += " and keyword: ";
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001099 errMsg += keywordName;
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001100
Santosh Puranikdedb5a62022-12-19 23:58:32 +05301101 std::ostringstream busStream;
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001102 for (uint16_t byte : backupValue)
Santosh Puranikdedb5a62022-12-19 23:58:32 +05301103 {
1104 busStream << std::setfill('0')
1105 << std::setw(2) << std::hex
1106 << "0x" << byte << " ";
1107 }
1108
1109 std::ostringstream vpdStream;
1110 for (uint16_t byte : kwdValue)
1111 {
1112 vpdStream << std::setfill('0')
1113 << std::setw(2) << std::hex
1114 << "0x" << byte << " ";
1115 }
1116
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001117 // data mismatch
1118 PelAdditionalData additionalData;
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001119
1120 additionalData.emplace("DESCRIPTION", errMsg);
Santosh Puranikdedb5a62022-12-19 23:58:32 +05301121 additionalData.emplace(
Priyanga Ramasamy24942232023-01-05 04:54:59 -06001122 "Value read from Backup: ",
1123 busStream.str());
1124 additionalData.emplace(
1125 "Value read from Primary: ",
Santosh Puranikdedb5a62022-12-19 23:58:32 +05301126 vpdStream.str());
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001127
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001128 createPEL(additionalData, PelSeverity::WARNING,
Priyanga Ramasamy24942232023-01-05 04:54:59 -06001129 errIntfForVPDMismatch, nullptr);
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001130
1131 if (!isBackupOnCache)
1132 {
1133 // Backing up or restoring from a hardware
1134 // path does not requires copying the backup
1135 // data to the VPD map, as this will result
1136 // in a mismatch between the primary VPD and
1137 // its cache.
1138 continue;
1139 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001140 }
Sunny Srivastava37992a62023-07-11 05:18:41 -05001141 else
1142 {
1143 // both the backup and primary data is
1144 // non-default and same. Nothing needs to be
1145 // done.
1146 continue;
1147 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001148 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001149
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001150 // If the backup is on the cache we need to copy the
1151 // backup data to the VPD map to ensure there is no
1152 // mimatch b/n them. So if backup data is not default,
1153 // then irrespective of primary data(default or other
1154 // than backup), copy the backup data to vpd map as we
1155 // don't need to change the backup data in either case
1156 // in the process of restoring system vpd.
1157 kwdValue = backupValue;
1158
1159 // If the backup data is on the base panel the restoring
1160 // of Backup VPD on to the system backplane VPD
1161 // file is done here not through the VPD manager code
1162 // path. This is to have the logic of restoring data on
1163 // to the cache & hardware in the same code path.
1164 if (!isBackupOnCache)
1165 {
1166 // copy backup VPD on to system backplane
1167 // EEPROM file.
1168 updateVpdDataOnHw(systemVpdFilePath, js, recordName,
1169 keywordName, backupDataInBinary);
1170 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001171 }
Priyanga Ramasamy952d6c52022-11-07 07:20:24 -06001172 else if (kwdDataInBinary == defaultValue &&
1173 get<2>(keywordInfo)) // Check isPELRequired is true
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001174 {
Priyanga Ramasamy24942232023-01-05 04:54:59 -06001175 string errMsg = "Found default value on both backup "
1176 "and primary VPD for record: ";
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001177 errMsg += (*it).first;
1178 errMsg += " and keyword: ";
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001179 errMsg += keywordName;
Sunny Srivastava37992a62023-07-11 05:18:41 -05001180 errMsg += ". Update primary VPD.";
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001181
Priyanga Ramasamy24942232023-01-05 04:54:59 -06001182 // mfg default on both backup and primary, log PEL
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001183 PelAdditionalData additionalData;
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001184 additionalData.emplace("DESCRIPTION", errMsg);
1185
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001186 createPEL(additionalData, PelSeverity::ERROR,
Priyanga Ramasamy24942232023-01-05 04:54:59 -06001187 errIntfForVPDDefault, nullptr);
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001188
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001189 continue;
1190 }
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001191 else if ((kwdDataInBinary != defaultValue) &&
1192 (!isBackupOnCache))
1193 {
1194 // update primary VPD on to backup VPD file
1195 updateVpdDataOnHw(systemVpdBackupPath, js,
1196 backupVpdRecName, backupVpdKwName,
1197 kwdDataInBinary);
1198
1199 // copy primary VPD to backup VPD to publish on
1200 // DBus
1201 backupVpdMap.find(backupVpdRecName)
1202 ->second.find(backupVpdKwName)
1203 ->second = kwdValue;
1204 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001205 }
1206 }
1207 }
1208 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001209}
1210
1211/**
alpana077ce68722021-07-25 13:23:59 -05001212 * @brief This checks for is this FRU a processor
1213 * And if yes, then checks for is this primary
1214 *
1215 * @param[in] js- vpd json to get the information about this FRU
1216 * @param[in] filePath- FRU vpd
1217 *
1218 * @return true/false
1219 */
1220bool isThisPrimaryProcessor(nlohmann::json& js, const string& filePath)
1221{
1222 bool isProcessor = false;
1223 bool isPrimary = false;
1224
1225 for (const auto& item : js["frus"][filePath])
1226 {
1227 if (item.find("extraInterfaces") != item.end())
1228 {
1229 for (const auto& eI : item["extraInterfaces"].items())
1230 {
1231 if (eI.key().find("Inventory.Item.Cpu") != string::npos)
1232 {
1233 isProcessor = true;
1234 }
1235 }
1236 }
1237
1238 if (isProcessor)
1239 {
1240 string cpuType = item.value("cpuType", "");
1241 if (cpuType == "primary")
1242 {
1243 isPrimary = true;
1244 }
1245 }
1246 }
1247
1248 return (isProcessor && isPrimary);
1249}
1250
1251/**
1252 * @brief This finds DIMM vpd in vpd json and enables them by binding the device
1253 * driver
1254 * @param[in] js- vpd json to iterate through and take action if it is DIMM
1255 */
1256void doEnableAllDimms(nlohmann::json& js)
1257{
1258 // iterate over each fru
1259 for (const auto& eachFru : js["frus"].items())
1260 {
1261 // skip the driver binding if eeprom already exists
1262 if (fs::exists(eachFru.key()))
1263 {
1264 continue;
1265 }
1266
1267 for (const auto& eachInventory : eachFru.value())
1268 {
1269 if (eachInventory.find("extraInterfaces") != eachInventory.end())
1270 {
1271 for (const auto& eI : eachInventory["extraInterfaces"].items())
1272 {
1273 if (eI.key().find("Inventory.Item.Dimm") != string::npos)
1274 {
1275 string dimmVpd = eachFru.key();
1276 // fetch it from
1277 // "/sys/bus/i2c/drivers/at24/414-0050/eeprom"
1278
1279 regex matchPatern("([0-9]+-[0-9]{4})");
1280 smatch matchFound;
1281 if (regex_search(dimmVpd, matchFound, matchPatern))
1282 {
1283 vector<string> i2cReg;
1284 boost::split(i2cReg, matchFound.str(0),
1285 boost::is_any_of("-"));
1286
1287 // remove 0s from begining
1288 const regex pattern("^0+(?!$)");
1289 for (auto& i : i2cReg)
1290 {
1291 i = regex_replace(i, pattern, "");
1292 }
1293
Jinu Joy Thomas63cce0f2024-01-29 00:47:47 -06001294 // For ISDIMM which uses ee1004 driver
1295 // the below is done
1296 size_t stringFound = dimmVpd.find("ee1004");
1297 if (stringFound != string::npos)
1298 {
1299 // echo ee1004 0x50 >
1300 // /sys/bus/i2c/devices/i2c-110/new_device
1301 string cmnd = "echo ee1004 0x" + i2cReg[1] +
1302 " > /sys/bus/i2c/devices/i2c-" +
1303 i2cReg[0] + "/new_device";
1304 executeCmd(cmnd);
1305 }
1306 else if (i2cReg.size() == 2)
alpana077ce68722021-07-25 13:23:59 -05001307 {
1308 // echo 24c32 0x50 >
1309 // /sys/bus/i2c/devices/i2c-16/new_device
1310 string cmnd = "echo 24c32 0x" + i2cReg[1] +
1311 " > /sys/bus/i2c/devices/i2c-" +
1312 i2cReg[0] + "/new_device";
alpana077ce68722021-07-25 13:23:59 -05001313 executeCmd(cmnd);
1314 }
1315 }
1316 }
1317 }
1318 }
1319 }
1320 }
1321}
1322
1323/**
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001324 * @brief Check if the given CPU is an IO only chip.
1325 * The CPU is termed as IO, whose all of the cores are bad and can never be
1326 * used. Those CPU chips can be used for IO purpose like connecting PCIe devices
1327 * etc., The CPU whose every cores are bad, can be identified from the CP00
1328 * record's PG keyword, only if all of the 8 EQs' value equals 0xE7F9FF. (1EQ
1329 * has 4 cores grouped together by sharing its cache memory.)
1330 * @param [in] pgKeyword - PG Keyword of CPU.
1331 * @return true if the given cpu is an IO, false otherwise.
1332 */
1333static bool isCPUIOGoodOnly(const string& pgKeyword)
1334{
1335 const unsigned char io[] = {0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9,
1336 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7,
1337 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
1338 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
1339 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
1340 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
1341 // IO.
1342 if (memcmp(io, pgKeyword.data() + 97, 24) == 0)
1343 {
1344 return true;
1345 }
1346
1347 // The CPU is not an IO
1348 return false;
1349}
1350
1351/**
Jinu Joy Thomas9a60c8b2024-01-16 22:52:25 -06001352 * @brief Function to bring MUX out of idle state
1353 *
1354 * This finds All the MUX defined in the system json and enables
1355 * them by setting the holdidle parameter to 0.
1356 * @param[in] js- system json to iterate through and take action
1357 */
1358void doEnableAllMuxChips(const nlohmann::json& js)
1359{
1360 // Do we have the mandatory "muxes" section?
1361 if (js.find("muxes") != js.end())
1362 {
1363 std::cout << "Enabling all the MUX on the system " << std::endl;
1364 // iterate over each MUX detail and enable them
1365 for (const auto& item : js["muxes"])
1366 {
1367 if (item.find("holdidlepath") != item.end())
1368 {
Jinu Joy Thomas63cce0f2024-01-29 00:47:47 -06001369 const std::string& holdidle = item["holdidlepath"];
Jinu Joy Thomas9a60c8b2024-01-16 22:52:25 -06001370 std::cout << "Setting holdidle state for " << holdidle
1371 << "to 0 " << std::endl;
1372 string cmd = "echo 0 > " + holdidle;
1373 executeCmd(cmd);
1374 }
1375 }
1376 std::cout << "Completed enabling all the MUX on the system "
1377 << std::endl;
1378 }
1379 else
1380 {
1381 std::cout << "No MUX was defined for the system" << std::endl;
1382 }
1383}
1384
1385/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301386 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301387 * This method invokes all the populateInterface functions
1388 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301389 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
1390 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301391 * @param[in] js - Inventory json object
1392 * @param[in] filePath - Path of the vpd file
1393 * @param[in] preIntrStr - Interface string
1394 */
1395template <typename T>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001396static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301397{
1398 inventory::InterfaceMap interfaces;
1399 inventory::ObjectMap objects;
1400 inventory::PropertyMap prop;
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001401 string ccinFromVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301402
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301403 bool isSystemVpd = (filePath == systemVpdFilePath);
1404 if constexpr (is_same<T, Parsed>::value)
1405 {
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001406 ccinFromVpd = getKwVal(vpdMap, "VINI", "CC");
1407 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1408 ::toupper);
1409
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301410 if (isSystemVpd)
1411 {
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301412 string mboardPath =
1413 js["frus"][filePath].at(0).value("inventoryPath", "");
1414
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001415 // Get the value of systemvpdBackupPath field from json
1416 const std::string& systemVpdBackupPath =
1417 js["frus"][filePath].at(0).value("systemVpdBackupPath", "");
1418
1419 if (systemVpdBackupPath.empty())
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301420 {
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001421 std::vector<std::string> interfaces = {motherBoardInterface};
1422 // call mapper to check for object path creation
1423 MapperResponse subTree =
1424 getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
1425
1426 // Attempt system VPD restore if we have a motherboard
1427 // object in the inventory.
1428 if ((subTree.size() != 0) &&
1429 (subTree.find(pimPath + mboardPath) != subTree.end()))
1430 {
1431 restoreSystemVPD(vpdMap, mboardPath, js);
1432 }
1433 else
1434 {
1435 log<level::ERR>("No object path found");
1436 }
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301437 }
1438 else
1439 {
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001440 restoreSystemVPD(vpdMap, mboardPath, js, false);
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301441 }
1442 }
alpana077ce68722021-07-25 13:23:59 -05001443 else
1444 {
1445 // check if it is processor vpd.
1446 auto isPrimaryCpu = isThisPrimaryProcessor(js, filePath);
1447
1448 if (isPrimaryCpu)
1449 {
1450 auto ddVersion = getKwVal(vpdMap, "CRP0", "DD");
1451
1452 auto chipVersion = atoi(ddVersion.substr(1, 2).c_str());
1453
1454 if (chipVersion >= 2)
1455 {
1456 doEnableAllDimms(js);
Santosh Puranik253fbe92022-10-06 22:38:09 +05301457 // Sleep for a few seconds to let the DIMM parses start
1458 using namespace std::chrono_literals;
1459 std::this_thread::sleep_for(5s);
alpana077ce68722021-07-25 13:23:59 -05001460 }
1461 }
1462 }
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301463 }
1464
Santosh Puranikf3e69682022-03-31 17:52:38 +05301465 auto processFactoryReset = false;
1466
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001467 if (isSystemVpd)
1468 {
1469 string systemJsonName{};
1470 if constexpr (is_same<T, Parsed>::value)
1471 {
1472 // pick the right system json
1473 systemJsonName = getSystemsJson(vpdMap);
1474 }
1475
1476 fs::path target = systemJsonName;
1477 fs::path link = INVENTORY_JSON_SYM_LINK;
1478
Santosh Puranikf3e69682022-03-31 17:52:38 +05301479 // If the symlink does not exist, we treat that as a factory reset
1480 processFactoryReset = !fs::exists(INVENTORY_JSON_SYM_LINK);
1481
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001482 // Create the directory for hosting the symlink
1483 fs::create_directories(VPD_FILES_PATH);
1484 // unlink the symlink previously created (if any)
1485 remove(INVENTORY_JSON_SYM_LINK);
1486 // create a new symlink based on the system
1487 fs::create_symlink(target, link);
1488
1489 // Reloading the json
1490 ifstream inventoryJson(link);
1491 js = json::parse(inventoryJson);
1492 inventoryJson.close();
1493 }
1494
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301495 for (const auto& item : js["frus"][filePath])
1496 {
1497 const auto& objectPath = item["inventoryPath"];
1498 sdbusplus::message::object_path object(objectPath);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001499
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001500 vector<string> ccinList;
1501 if (item.find("ccin") != item.end())
1502 {
1503 for (const auto& cc : item["ccin"])
1504 {
1505 string ccin = cc;
1506 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1507 ccinList.push_back(ccin);
1508 }
1509 }
1510
1511 if (!ccinFromVpd.empty() && !ccinList.empty() &&
1512 (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1513 ccinList.end()))
1514 {
1515 continue;
1516 }
1517
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001518 if ((isSystemVpd) || (item.value("noprime", false)))
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301519 {
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001520 // Populate one time properties for the system VPD and its sub-frus
1521 // and for other non-primeable frus.
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301522 // For the remaining FRUs, this will get handled as a part of
1523 // priming the inventory.
1524 setOneTimeProperties(objectPath, interfaces);
1525 }
1526
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301527 // Populate the VPD keywords and the common interfaces only if we
1528 // are asked to inherit that data from the VPD, else only add the
1529 // extraInterfaces.
1530 if (item.value("inherit", true))
1531 {
Alpana Kumari58e22142020-05-05 00:22:12 -05001532 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301533 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301534 // Each record in the VPD becomes an interface and all
1535 // keyword within the record are properties under that
1536 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301537 for (const auto& record : vpdMap)
1538 {
1539 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001540 record.second, ipzVpdInf + record.first, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301541 }
1542 }
Alpana Kumari58e22142020-05-05 00:22:12 -05001543 else if constexpr (is_same<T, KeywordVpdMap>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301544 {
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001545 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301546 }
Santosh Puranik88edeb62020-03-02 12:00:09 +05301547 if (js.find("commonInterfaces") != js.end())
1548 {
1549 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
1550 isSystemVpd);
1551 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301552 }
Santosh Puranik0859eb62020-03-16 02:56:29 -05001553 else
1554 {
1555 // Check if we have been asked to inherit specific record(s)
Alpana Kumari58e22142020-05-05 00:22:12 -05001556 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik0859eb62020-03-16 02:56:29 -05001557 {
1558 if (item.find("copyRecords") != item.end())
1559 {
1560 for (const auto& record : item["copyRecords"])
1561 {
1562 const string& recordName = record;
1563 if (vpdMap.find(recordName) != vpdMap.end())
1564 {
1565 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001566 vpdMap.at(recordName), ipzVpdInf + recordName,
Santosh Puranik0859eb62020-03-16 02:56:29 -05001567 interfaces);
1568 }
1569 }
1570 }
1571 }
1572 }
Santosh Puranik32c46502022-02-10 08:55:07 +05301573 // Populate interfaces and properties that are common to every FRU
1574 // and additional interface that might be defined on a per-FRU
1575 // basis.
1576 if (item.find("extraInterfaces") != item.end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301577 {
Santosh Puranik32c46502022-02-10 08:55:07 +05301578 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
1579 isSystemVpd);
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001580 if constexpr (is_same<T, Parsed>::value)
1581 {
1582 if (item["extraInterfaces"].find(
1583 "xyz.openbmc_project.Inventory.Item.Cpu") !=
1584 item["extraInterfaces"].end())
1585 {
1586 if (isCPUIOGoodOnly(getKwVal(vpdMap, "CP00", "PG")))
1587 {
Priyanga Ramasamy2c607a92022-04-08 00:30:17 -05001588 interfaces[invItemIntf]["PrettyName"] = "IO Module";
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001589 }
1590 }
1591 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301592 }
Priyanga Ramasamye358acb2022-03-21 14:21:50 -05001593
1594 // embedded property(true or false) says whether the subfru is embedded
1595 // into the parent fru (or) not. VPD sets Present property only for
1596 // embedded frus. If the subfru is not an embedded FRU, the subfru may
1597 // or may not be physically present. Those non embedded frus will always
1598 // have Present=false irrespective of its physical presence or absence.
1599 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
1600 // Present to true for such sub frus.
1601 // Eg: ethernet port is embedded into bmc card. So set Present to true
1602 // for such sub frus. Also donot populate present property for embedded
1603 // subfru which is synthesized. Currently there is no subfru which are
1604 // both embedded and synthesized. But still the case is handled here.
1605 if ((item.value("embedded", true)) &&
1606 (!item.value("synthesized", false)))
1607 {
Priyanga Ramasamyaca61372023-01-24 08:02:28 -06001608 // Check if its required to handle presence for this FRU.
1609 if (item.value("handlePresence", true))
1610 {
1611 inventory::PropertyMap presProp;
1612 presProp.emplace("Present", true);
1613 insertOrMerge(interfaces, invItemIntf, move(presProp));
1614 }
Priyanga Ramasamye358acb2022-03-21 14:21:50 -05001615 }
Priyanga Ramasamyaa8a8932022-01-27 09:12:41 -06001616
Santosh Puranikf3e69682022-03-31 17:52:38 +05301617 if constexpr (is_same<T, Parsed>::value)
1618 {
1619 // Restore asset tag, if needed
1620 if (processFactoryReset && objectPath == "/system")
1621 {
1622 fillAssetTag(interfaces, vpdMap);
1623 }
1624 }
1625
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301626 objects.emplace(move(object), move(interfaces));
1627 }
1628
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301629 if (isSystemVpd)
1630 {
1631 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
1632 objects.insert(primeObject.begin(), primeObject.end());
Alpana Kumari65b83602020-09-01 00:24:56 -05001633
Alpana Kumarif05effd2021-04-07 07:32:53 -05001634 // set the U-boot environment variable for device-tree
1635 if constexpr (is_same<T, Parsed>::value)
1636 {
Santosh Puranike5f177a2022-01-24 20:14:46 +05301637 setDevTreeEnv(fs::path(getSystemsJson(vpdMap)).filename());
Alpana Kumarif05effd2021-04-07 07:32:53 -05001638 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301639 }
1640
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301641 // Notify PIM
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05001642 common::utility::callPIM(move(objects));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301643}
1644
1645int main(int argc, char** argv)
1646{
1647 int rc = 0;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001648 json js{};
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001649 Binary vpdVector{};
1650 string file{};
jinuthomasf457a3e2023-04-13 12:22:48 -05001651 string driver{};
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001652 // map to hold additional data in case of logging pel
1653 PelAdditionalData additionalData{};
1654
1655 // this is needed to hold base fru inventory path in case there is ECC or
1656 // vpd exception while parsing the file
1657 std::string baseFruInventoryPath = {};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301658
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001659 // It holds the backup EEPROM file path for the system backplane's critical
1660 // data
1661 std::string systemVpdBackupPath{};
1662
1663 // It holds the inventory path of backup EEPROM file
1664 std::string backupVpdInvPath{};
1665
1666 bool isSystemVpd = false;
1667
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001668 // severity for PEL
1669 PelSeverity pelSeverity = PelSeverity::WARNING;
1670
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301671 try
1672 {
jinuthomasf457a3e2023-04-13 12:22:48 -05001673 App app{"ibm-read-vpd - App to read IPZ/Jedec format VPD, parse it and "
1674 "store it in DBUS"};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301675
1676 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
Alpana Kumari2f793042020-08-18 05:51:03 -05001677 ->required();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301678
jinuthomasf457a3e2023-04-13 12:22:48 -05001679 app.add_option("--driver", driver,
1680 "Driver used by kernel (at24,at25,ee1004)")
1681 ->required();
1682
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301683 CLI11_PARSE(app, argc, argv);
1684
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001685 // PEL severity should be ERROR in case of any system VPD failure
1686 if (file == systemVpdFilePath)
1687 {
1688 pelSeverity = PelSeverity::ERROR;
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001689 isSystemVpd = true;
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001690 }
1691
jinuthomasf457a3e2023-04-13 12:22:48 -05001692 // Check if input file is not empty.
1693 if ((file.empty()) || (driver.empty()))
1694 {
1695 std::cerr << "Encountered empty input parameter file [" << file
1696 << "] driver [" << driver << "]" << std::endl;
1697 return 0;
1698 }
1699
1700 // Check if currently supported driver or not
1701 if ((driver != at24driver) && (driver != at25driver) &&
1702 (driver != ee1004driver))
1703 {
1704 std::cerr << "The driver [" << driver << "] is not supported."
1705 << std::endl;
1706 return 0;
1707 }
1708
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301709 auto jsonToParse = INVENTORY_JSON_DEFAULT;
1710
1711 // If the symlink exists, it means it has been setup for us, switch the
1712 // path
1713 if (fs::exists(INVENTORY_JSON_SYM_LINK))
1714 {
1715 jsonToParse = INVENTORY_JSON_SYM_LINK;
1716 }
1717
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301718 // Make sure that the file path we get is for a supported EEPROM
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301719 ifstream inventoryJson(jsonToParse);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001720 if (!inventoryJson)
1721 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001722 throw(VpdJsonException("Failed to access Json path", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001723 }
1724
1725 try
1726 {
1727 js = json::parse(inventoryJson);
1728 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001729 catch (const json::parse_error& ex)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001730 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001731 throw(VpdJsonException("Json parsing failed", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001732 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301733
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301734 // Do we have the mandatory "frus" section?
1735 if (js.find("frus") == js.end())
1736 {
1737 throw(VpdJsonException("FRUs section not found in JSON",
1738 jsonToParse));
1739 }
1740
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301741 // Check if it's a udev path - patterned as(/ahb/ahb:apb/ahb:apb:bus@)
1742 if (file.find("/ahb:apb") != string::npos)
1743 {
1744 // Translate udev path to a generic /sys/bus/.. file path.
jinuthomasf457a3e2023-04-13 12:22:48 -05001745 udevToGenericPath(file, driver);
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301746
1747 if ((js["frus"].find(file) != js["frus"].end()) &&
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301748 (file == systemVpdFilePath))
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301749 {
jinuthomasf457a3e2023-04-13 12:22:48 -05001750 std::cout << "We have already collected system VPD, skiping."
1751 << std::endl;
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301752 return 0;
1753 }
1754 }
1755
Jinu Joy Thomas2cb67692024-01-30 10:30:26 -06001756 // Enable all mux which are used for connecting to the i2c on the pcie
1757 // slots for pcie cards. These are not enabled by kernel due to an issue
1758 // seen with Castello cards, where the i2c line hangs on a probe.
1759 // To run it only once have kept it under System vpd check.
1760 // we need to run this on all BMC reboots so kept here
1761 if (file == systemVpdFilePath)
1762 {
1763 doEnableAllMuxChips(js);
1764 }
1765
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301766 if (file.empty())
1767 {
jinuthomasf457a3e2023-04-13 12:22:48 -05001768 std::cerr << "The EEPROM path <" << file << "> is not valid.";
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301769 return 0;
1770 }
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301771 if (js["frus"].find(file) == js["frus"].end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301772 {
jinuthomasf457a3e2023-04-13 12:22:48 -05001773 std::cerr << "The EEPROM path [" << file
1774 << "] is not found in the json." << std::endl;
Santosh Puranik88edeb62020-03-02 12:00:09 +05301775 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301776 }
1777
Alpana Kumari2f793042020-08-18 05:51:03 -05001778 if (!fs::exists(file))
1779 {
jinuthomasf457a3e2023-04-13 12:22:48 -05001780 std::cout << "Device path: " << file
1781 << " does not exist. Spurious udev event? Exiting."
1782 << std::endl;
Alpana Kumari2f793042020-08-18 05:51:03 -05001783 return 0;
1784 }
1785
Santosh Puranikdedb5a62022-12-19 23:58:32 +05301786 // In case of system VPD it will already be filled, Don't have to
1787 // overwrite that.
1788 if (baseFruInventoryPath.empty())
1789 {
1790 baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
1791 }
1792
Santosh Puranik85893752020-11-10 21:31:43 +05301793 // Check if we can read the VPD file based on the power state
Santosh Puranik27a5e952021-10-07 22:08:01 -05001794 // We skip reading VPD when the power is ON in two scenarios:
Santosh Puranik31d50fa2022-04-04 12:04:37 +05301795 // 1) The eeprom we are trying to read is that of the system VPD and the
1796 // JSON symlink is already setup (the symlink's existence tells us we
1797 // are not coming out of a factory reset)
1798 // 2) The JSON tells us that the FRU EEPROM cannot be
1799 // read when we are powered ON.
Santosh Puranik27a5e952021-10-07 22:08:01 -05001800 if (js["frus"][file].at(0).value("powerOffOnly", false) ||
Santosh Puranik31d50fa2022-04-04 12:04:37 +05301801 (file == systemVpdFilePath && fs::exists(INVENTORY_JSON_SYM_LINK)))
Santosh Puranik85893752020-11-10 21:31:43 +05301802 {
1803 if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
1804 getPowerState())
1805 {
jinuthomasf457a3e2023-04-13 12:22:48 -05001806 std::cout << "This VPD cannot be read when power is ON"
1807 << std::endl;
Santosh Puranik85893752020-11-10 21:31:43 +05301808 return 0;
1809 }
1810 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001811
Santosh Puranike9c57532022-03-15 16:51:51 +05301812 // Check if this VPD should be recollected at all
1813 if (!needsRecollection(js, file))
1814 {
jinuthomasf457a3e2023-04-13 12:22:48 -05001815 std::cout << "Skip VPD recollection for: " << file << std::endl;
Santosh Puranike9c57532022-03-15 16:51:51 +05301816 return 0;
1817 }
1818
Alpana Kumari2f793042020-08-18 05:51:03 -05001819 try
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301820 {
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001821 variant<KeywordVpdMap, Store> parseResult;
Sunny Srivastava37992a62023-07-11 05:18:41 -05001822 parseResult = parseVpdFile(file, js);
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001823
1824 if (isSystemVpd)
girik18bb9852022-11-16 05:48:13 -06001825 {
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001826 // Get the value of systemVpdBackupPath field from json
1827 systemVpdBackupPath = js["frus"][systemVpdFilePath].at(0).value(
1828 "systemVpdBackupPath", "");
1829
1830 if (!systemVpdBackupPath.empty())
girik18bb9852022-11-16 05:48:13 -06001831 {
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001832 backupVpdInvPath =
1833 js["frus"][systemVpdBackupPath][0]["inventoryPath"]
1834 .get_ref<const nlohmann::json::string_t&>();
girik18bb9852022-11-16 05:48:13 -06001835 }
1836 }
SunnySrivastava19849a195542020-09-07 06:04:50 -05001837
Alpana Kumari2f793042020-08-18 05:51:03 -05001838 if (auto pVal = get_if<Store>(&parseResult))
1839 {
1840 populateDbus(pVal->getVpdMap(), js, file);
1841 }
1842 else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
1843 {
1844 populateDbus(*pVal, js, file);
1845 }
Alpana Kumari2f793042020-08-18 05:51:03 -05001846 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001847 catch (const exception& e)
Alpana Kumari2f793042020-08-18 05:51:03 -05001848 {
Sunny Srivastava37992a62023-07-11 05:18:41 -05001849 if (!systemVpdBackupPath.empty())
1850 {
1851 file = systemVpdBackupPath;
1852 baseFruInventoryPath = backupVpdInvPath;
1853 }
1854
Alpana Kumari735dee92022-03-25 01:24:40 -05001855 executePostFailAction(js, file);
PriyangaRamasamya504c3e2020-12-06 12:14:52 -06001856 throw;
Alpana Kumari2f793042020-08-18 05:51:03 -05001857 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301858 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001859 catch (const VpdJsonException& ex)
1860 {
1861 additionalData.emplace("JSON_PATH", ex.getJsonPath());
1862 additionalData.emplace("DESCRIPTION", ex.what());
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -05001863 createPEL(additionalData, pelSeverity, errIntfForJsonFailure, nullptr);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001864
jinuthomasf457a3e2023-04-13 12:22:48 -05001865 std::cerr << ex.what() << "\n";
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001866 rc = -1;
1867 }
1868 catch (const VpdEccException& ex)
1869 {
1870 additionalData.emplace("DESCRIPTION", "ECC check failed");
1871 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1872 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -05001873 createPEL(additionalData, pelSeverity, errIntfForEccCheckFail, nullptr);
Kantesh Nagaradder38ee9c82023-04-07 00:58:12 -05001874
1875 if (systemVpdBackupPath.empty())
1876 {
1877 dumpBadVpd(file, vpdVector);
1878 }
1879
jinuthomasf457a3e2023-04-13 12:22:48 -05001880 std::cerr << ex.what() << "\n";
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001881 rc = -1;
1882 }
1883 catch (const VpdDataException& ex)
1884 {
alpana075cb3b1f2021-12-16 11:19:36 -06001885 if (isThisPcieOnPass1planar(js, file))
1886 {
jinuthomasf457a3e2023-04-13 12:22:48 -05001887 std::cout << "Pcie_device [" << file
1888 << "]'s VPD is not valid on PASS1 planar.Ignoring.\n";
alpana075cb3b1f2021-12-16 11:19:36 -06001889 rc = 0;
1890 }
Santosh Puranik53b38ed2022-04-10 23:15:22 +05301891 else if (!(isPresent(js, file).value_or(true)))
1892 {
jinuthomasf457a3e2023-04-13 12:22:48 -05001893 std::cout << "FRU at: " << file
1894 << " is not detected present. Ignore parser error.\n";
Santosh Puranik53b38ed2022-04-10 23:15:22 +05301895 rc = 0;
1896 }
alpana075cb3b1f2021-12-16 11:19:36 -06001897 else
1898 {
1899 string errorMsg =
1900 "VPD file is either empty or invalid. Parser failed for [";
1901 errorMsg += file;
1902 errorMsg += "], with error = " + std::string(ex.what());
1903
1904 additionalData.emplace("DESCRIPTION", errorMsg);
1905 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1906 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -05001907 createPEL(additionalData, pelSeverity, errIntfForInvalidVPD,
1908 nullptr);
alpana075cb3b1f2021-12-16 11:19:36 -06001909
1910 rc = -1;
1911 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001912 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001913 catch (const exception& e)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301914 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001915 dumpBadVpd(file, vpdVector);
jinuthomasf457a3e2023-04-13 12:22:48 -05001916 std::cerr << e.what() << "\n";
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301917 rc = -1;
1918 }
1919
1920 return rc;
Patrick Williamsc78d8872023-05-10 07:50:56 -05001921}