blob: 7cc0194e73fc81c8629aeb1576f85ea96b954173 [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"
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05005#include "ibm_vpd_utils.hpp"
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05006#include "ipz_parser.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05307#include "keyword_vpd_parser.hpp"
Alpana Kumaria00936f2020-04-14 07:15:46 -05008#include "memory_vpd_parser.hpp"
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05009#include "parser_factory.hpp"
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -050010#include "vpd_exceptions.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053011
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050012#include <assert.h>
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -050013#include <ctype.h>
14
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053015#include <CLI/CLI.hpp>
Santosh Puranik88edeb62020-03-02 12:00:09 +053016#include <algorithm>
alpana077ce68722021-07-25 13:23:59 -050017#include <boost/algorithm/string.hpp>
Alpana Kumari65b83602020-09-01 00:24:56 -050018#include <cstdarg>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053019#include <exception>
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053020#include <filesystem>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053021#include <fstream>
Alpana Kumari2f793042020-08-18 05:51:03 -050022#include <gpiod.hpp>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053023#include <iostream>
24#include <iterator>
25#include <nlohmann/json.hpp>
Andrew Geissler280197e2020-12-08 20:51:49 -060026#include <phosphor-logging/log.hpp>
alpana077ce68722021-07-25 13:23:59 -050027#include <regex>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053028
29using namespace std;
30using namespace openpower::vpd;
31using namespace CLI;
32using namespace vpd::keyword::parser;
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053033using namespace openpower::vpd::constants;
34namespace fs = filesystem;
35using json = nlohmann::json;
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050036using namespace openpower::vpd::parser::factory;
SunnySrivastava1984945a02d2020-05-06 01:55:41 -050037using namespace openpower::vpd::inventory;
Alpana Kumaria00936f2020-04-14 07:15:46 -050038using namespace openpower::vpd::memory::parser;
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050039using namespace openpower::vpd::parser::interface;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -050040using namespace openpower::vpd::exceptions;
Andrew Geissler280197e2020-12-08 20:51:49 -060041using namespace phosphor::logging;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053042
Sunny Srivastava3c244142022-01-11 08:47:04 -060043// Map to hold record, kwd pair which can be re-stored at standby.
44// The list of keywords for VSYS record is as per the S0 system. Should
45// be updated for another type of systems
46static const std::unordered_map<std::string, std::vector<std::string>>
47 svpdKwdMap{{"VSYS", {"BR", "TM", "SE", "SU", "RB"}},
48 {"VCEN", {"FC", "SE"}},
49 {"LXR0", {"LX"}}};
50
Santosh Puranik88edeb62020-03-02 12:00:09 +053051/**
Santosh Puranik85893752020-11-10 21:31:43 +053052 * @brief Returns the power state for chassis0
53 */
54static auto getPowerState()
55{
56 // TODO: How do we handle multiple chassis?
57 string powerState{};
58 auto bus = sdbusplus::bus::new_default();
59 auto properties =
60 bus.new_method_call("xyz.openbmc_project.State.Chassis",
61 "/xyz/openbmc_project/state/chassis0",
62 "org.freedesktop.DBus.Properties", "Get");
63 properties.append("xyz.openbmc_project.State.Chassis");
64 properties.append("CurrentPowerState");
65 auto result = bus.call(properties);
66 if (!result.is_method_error())
67 {
68 variant<string> val;
69 result.read(val);
70 if (auto pVal = get_if<string>(&val))
71 {
72 powerState = *pVal;
73 }
74 }
75 cout << "Power state is: " << powerState << endl;
76 return powerState;
77}
78
79/**
Santosh Puranik88edeb62020-03-02 12:00:09 +053080 * @brief Expands location codes
81 */
82static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
83 bool isSystemVpd)
84{
85 auto expanded{unexpanded};
86 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
87 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
88 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
89 size_t idx = expanded.find("fcs");
90 try
91 {
92 if (idx != string::npos)
93 {
94 string fc{};
95 string se{};
96 if (isSystemVpd)
97 {
98 const auto& fcData = vpdMap.at("VCEN").at("FC");
99 const auto& seData = vpdMap.at("VCEN").at("SE");
100 fc = string(fcData.data(), fcData.size());
101 se = string(seData.data(), seData.size());
102 }
103 else
104 {
105 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
106 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
107 }
108
Alpana Kumari81671f62021-02-10 02:21:59 -0600109 // TODO: See if ND0 can be placed in the JSON
110 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND0." + se);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530111 }
112 else
113 {
114 idx = expanded.find("mts");
115 if (idx != string::npos)
116 {
117 string mt{};
118 string se{};
119 if (isSystemVpd)
120 {
121 const auto& mtData = vpdMap.at("VSYS").at("TM");
122 const auto& seData = vpdMap.at("VSYS").at("SE");
123 mt = string(mtData.data(), mtData.size());
124 se = string(seData.data(), seData.size());
125 }
126 else
127 {
128 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
129 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
130 }
131
132 replace(mt.begin(), mt.end(), '-', '.');
133 expanded.replace(idx, 3, mt + "." + se);
134 }
135 }
136 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500137 catch (const exception& e)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530138 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500139 cerr << "Failed to expand location code with exception: " << e.what()
140 << "\n";
Santosh Puranik88edeb62020-03-02 12:00:09 +0530141 }
142 return expanded;
143}
Alpana Kumari2f793042020-08-18 05:51:03 -0500144
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530145/**
146 * @brief Populate FRU specific interfaces.
147 *
148 * This is a common method which handles both
149 * ipz and keyword specific interfaces thus,
150 * reducing the code redundancy.
151 * @param[in] map - Reference to the innermost keyword-value map.
152 * @param[in] preIntrStr - Reference to the interface string.
153 * @param[out] interfaces - Reference to interface map.
154 */
155template <typename T>
156static void populateFruSpecificInterfaces(const T& map,
157 const string& preIntrStr,
158 inventory::InterfaceMap& interfaces)
159{
160 inventory::PropertyMap prop;
161
162 for (const auto& kwVal : map)
163 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530164 auto kw = kwVal.first;
165
166 if (kw[0] == '#')
167 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500168 kw = string("PD_") + kw[1];
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530169 }
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500170 else if (isdigit(kw[0]))
171 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500172 kw = string("N_") + kw;
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500173 }
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000174 if constexpr (is_same<T, KeywordVpdMap>::value)
175 {
176 if (get_if<Binary>(&kwVal.second))
177 {
178 Binary vec(get_if<Binary>(&kwVal.second)->begin(),
179 get_if<Binary>(&kwVal.second)->end());
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000180 prop.emplace(move(kw), move(vec));
181 }
182 else
183 {
184 if (kw == "MemorySizeInKB")
185 {
186 inventory::PropertyMap memProp;
187 auto memVal = get_if<size_t>(&kwVal.second);
188 if (memVal)
189 {
190 memProp.emplace(move(kw),
191 ((*memVal) * CONVERT_MB_TO_KB));
192 interfaces.emplace(
193 "xyz.openbmc_project.Inventory.Item.Dimm",
194 move(memProp));
195 }
196 else
197 {
198 cerr << "MemorySizeInKB value not found in vpd map\n";
199 }
200 }
201 }
202 }
203 else
204 {
205 Binary vec(kwVal.second.begin(), kwVal.second.end());
206 prop.emplace(move(kw), move(vec));
207 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530208 }
209
210 interfaces.emplace(preIntrStr, move(prop));
211}
212
213/**
214 * @brief Populate Interfaces.
215 *
216 * This method populates common and extra interfaces to dbus.
217 * @param[in] js - json object
218 * @param[out] interfaces - Reference to interface map
219 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530220 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530221 */
222template <typename T>
223static void populateInterfaces(const nlohmann::json& js,
224 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530225 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530226{
227 for (const auto& ifs : js.items())
228 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530229 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530230 inventory::PropertyMap props;
231
232 for (const auto& itr : ifs.value().items())
233 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530234 const string& busProp = itr.key();
235
Alpana Kumari31970de2020-02-17 06:49:57 -0600236 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530237 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530238 props.emplace(busProp, itr.value().get<bool>());
239 }
240 else if (itr.value().is_string())
241 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500242 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530243 {
244 if (busProp == "LocationCode" &&
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000245 inf == IBM_LOCATION_CODE_INF)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530246 {
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000247 // TODO deprecate the com.ibm interface later
Santosh Puranik88edeb62020-03-02 12:00:09 +0530248 auto prop = expandLocationCode(
249 itr.value().get<string>(), vpdMap, isSystemVpd);
250 props.emplace(busProp, prop);
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000251 interfaces.emplace(XYZ_LOCATION_CODE_INF, props);
Priyanga Ramasamy69f76022022-01-05 07:10:36 +0000252 interfaces.emplace(IBM_LOCATION_CODE_INF, props);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530253 }
254 else
255 {
256 props.emplace(busProp, itr.value().get<string>());
257 }
258 }
259 else
260 {
261 props.emplace(busProp, itr.value().get<string>());
262 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530263 }
Santosh Puraniked609af2021-06-21 11:30:07 +0530264 else if (itr.value().is_array())
265 {
266 try
267 {
268 props.emplace(busProp, itr.value().get<Binary>());
269 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500270 catch (const nlohmann::detail::type_error& e)
Santosh Puraniked609af2021-06-21 11:30:07 +0530271 {
272 std::cerr << "Type exception: " << e.what() << "\n";
273 // Ignore any type errors
274 }
275 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600276 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530277 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600278 const string& rec = itr.value().value("recordName", "");
279 const string& kw = itr.value().value("keywordName", "");
280 const string& encoding = itr.value().value("encoding", "");
281
Alpana Kumari58e22142020-05-05 00:22:12 -0500282 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530283 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530284 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
285 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600286 {
287 auto encoded =
288 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530289 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600290 }
291 }
Alpana Kumari58e22142020-05-05 00:22:12 -0500292 else if constexpr (is_same<T, KeywordVpdMap>::value)
Alpana Kumari31970de2020-02-17 06:49:57 -0600293 {
294 if (!kw.empty() && vpdMap.count(kw))
295 {
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000296 auto kwValue = get_if<Binary>(&vpdMap.at(kw));
297 auto uintValue = get_if<size_t>(&vpdMap.at(kw));
298
299 if (kwValue)
300 {
301 auto prop =
302 string((*kwValue).begin(), (*kwValue).end());
303
304 auto encoded = encodeKeyword(prop, encoding);
305
306 props.emplace(busProp, encoded);
307 }
308 else if (uintValue)
309 {
310 props.emplace(busProp, *uintValue);
311 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600312 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530313 }
314 }
Matt Spinlerb1e64bb2021-09-08 09:57:48 -0500315 else if (itr.value().is_number())
316 {
317 // For now assume the value is a size_t. In the future it would
318 // be nice to come up with a way to get the type from the JSON.
319 props.emplace(busProp, itr.value().get<size_t>());
320 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530321 }
322 interfaces.emplace(inf, move(props));
323 }
324}
325
Priyanga Ramasamy37233782021-12-09 03:14:02 -0600326/*API to reset EEPROM pointer to a safe position to avoid VPD corruption.
327 * Currently do reset only for DIMM VPD.*/
328static void resetEEPROMPointer(const nlohmann::json& js, const string& file,
329 ifstream& vpdFile)
330{
331 for (const auto& item : js["frus"][file])
332 {
333 if (item.find("extraInterfaces") != item.end())
334 {
335 if (item["extraInterfaces"].find(
336 "xyz.openbmc_project.Inventory.Item.Dimm") !=
337 item["extraInterfaces"].end())
338 {
339 // moves the EEPROM pointer to 2048 'th byte.
340 vpdFile.seekg(2047, std::ios::beg);
341 // Read that byte and discard - to affirm the move
342 // operation.
343 char ch;
344 vpdFile.read(&ch, sizeof(ch));
345 }
346 return;
347 }
348 }
349}
350
Alpana Kumari2f793042020-08-18 05:51:03 -0500351static Binary getVpdDataInVector(const nlohmann::json& js, const string& file)
Alpana Kumari58e22142020-05-05 00:22:12 -0500352{
353 uint32_t offset = 0;
354 // check if offset present?
355 for (const auto& item : js["frus"][file])
356 {
357 if (item.find("offset") != item.end())
358 {
359 offset = item["offset"];
360 }
361 }
362
363 // TODO: Figure out a better way to get max possible VPD size.
Priyanga Ramasamy3c2a2b92021-12-22 00:09:38 -0600364 auto maxVPDSize = std::min(std::filesystem::file_size(file),
365 static_cast<uintmax_t>(65504));
366
Alpana Kumari58e22142020-05-05 00:22:12 -0500367 Binary vpdVector;
Priyanga Ramasamy3c2a2b92021-12-22 00:09:38 -0600368 vpdVector.resize(maxVPDSize);
Alpana Kumari58e22142020-05-05 00:22:12 -0500369 ifstream vpdFile;
370 vpdFile.open(file, ios::binary);
371
372 vpdFile.seekg(offset, ios_base::cur);
Priyanga Ramasamy3c2a2b92021-12-22 00:09:38 -0600373 vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), maxVPDSize);
Alpana Kumari58e22142020-05-05 00:22:12 -0500374 vpdVector.resize(vpdFile.gcount());
375
Priyanga Ramasamy37233782021-12-09 03:14:02 -0600376 resetEEPROMPointer(js, file, vpdFile);
377
Alpana Kumari58e22142020-05-05 00:22:12 -0500378 return vpdVector;
379}
380
Alpana Kumari2f793042020-08-18 05:51:03 -0500381/** This API will be called at the end of VPD collection to perform any post
382 * actions.
383 *
384 * @param[in] json - json object
385 * @param[in] file - eeprom file path
386 */
387static void postFailAction(const nlohmann::json& json, const string& file)
388{
389 if ((json["frus"][file].at(0)).find("postActionFail") ==
390 json["frus"][file].at(0).end())
391 {
392 return;
393 }
394
395 uint8_t pinValue = 0;
396 string pinName;
397
398 for (const auto& postAction :
399 (json["frus"][file].at(0))["postActionFail"].items())
400 {
401 if (postAction.key() == "pin")
402 {
403 pinName = postAction.value();
404 }
405 else if (postAction.key() == "value")
406 {
407 // Get the value to set
408 pinValue = postAction.value();
409 }
410 }
411
412 cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
413
414 try
415 {
416 gpiod::line outputLine = gpiod::find_line(pinName);
417
418 if (!outputLine)
419 {
420 cout << "Couldn't find output line:" << pinName
421 << " on GPIO. Skipping...\n";
422
423 return;
424 }
425 outputLine.request(
426 {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
427 pinValue);
428 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500429 catch (const system_error&)
Alpana Kumari2f793042020-08-18 05:51:03 -0500430 {
431 cerr << "Failed to set post-action GPIO" << endl;
432 }
433}
434
435/** Performs any pre-action needed to get the FRU setup for collection.
436 *
437 * @param[in] json - json object
438 * @param[in] file - eeprom file path
439 */
440static void preAction(const nlohmann::json& json, const string& file)
441{
442 if ((json["frus"][file].at(0)).find("preAction") ==
443 json["frus"][file].at(0).end())
444 {
445 return;
446 }
447
448 uint8_t pinValue = 0;
449 string pinName;
450
451 for (const auto& postAction :
452 (json["frus"][file].at(0))["preAction"].items())
453 {
454 if (postAction.key() == "pin")
455 {
456 pinName = postAction.value();
457 }
458 else if (postAction.key() == "value")
459 {
460 // Get the value to set
461 pinValue = postAction.value();
462 }
463 }
464
465 cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
466 try
467 {
468 gpiod::line outputLine = gpiod::find_line(pinName);
469
470 if (!outputLine)
471 {
472 cout << "Couldn't find output line:" << pinName
473 << " on GPIO. Skipping...\n";
474
475 return;
476 }
477 outputLine.request(
478 {"FRU pre-action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
479 pinValue);
480 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500481 catch (const system_error&)
Alpana Kumari2f793042020-08-18 05:51:03 -0500482 {
483 cerr << "Failed to set pre-action GPIO" << endl;
484 return;
485 }
486
487 // Now bind the device
Alpana Kumari4c3bf5b2021-09-16 07:24:58 -0500488 string bind = json["frus"][file].at(0).value("devAddress", "");
Alpana Kumari2f793042020-08-18 05:51:03 -0500489 cout << "Binding device " << bind << endl;
490 string bindCmd = string("echo \"") + bind +
491 string("\" > /sys/bus/i2c/drivers/at24/bind");
492 cout << bindCmd << endl;
493 executeCmd(bindCmd);
494
495 // Check if device showed up (test for file)
496 if (!fs::exists(file))
497 {
498 cout << "EEPROM " << file << " does not exist. Take failure action"
499 << endl;
500 // If not, then take failure postAction
501 postFailAction(json, file);
502 }
503}
504
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530505/**
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530506 * @brief Set certain one time properties in the inventory
507 * Use this function to insert the Functional and Enabled properties into the
508 * inventory map. This function first checks if the object in question already
509 * has these properties hosted on D-Bus, if the property is already there, it is
510 * not modified, hence the name "one time". If the property is not already
511 * present, it will be added to the map with a suitable default value (true for
512 * Functional and false for Enabled)
513 *
514 * @param[in] object - The inventory D-Bus obejct without the inventory prefix.
515 * @param[inout] interfaces - Reference to a map of inventory interfaces to
516 * which the properties will be attached.
517 */
518static void setOneTimeProperties(const std::string& object,
519 inventory::InterfaceMap& interfaces)
520{
521 auto bus = sdbusplus::bus::new_default();
522 auto objectPath = INVENTORY_PATH + object;
523 auto prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
524 objectPath.c_str(),
525 "org.freedesktop.DBus.Properties", "Get");
526 prop.append("xyz.openbmc_project.State.Decorator.OperationalStatus");
527 prop.append("Functional");
528 try
529 {
530 auto result = bus.call(prop);
531 }
532 catch (const sdbusplus::exception::SdBusError& e)
533 {
534 // Treat as property unavailable
535 inventory::PropertyMap prop;
536 prop.emplace("Functional", true);
537 interfaces.emplace(
538 "xyz.openbmc_project.State.Decorator.OperationalStatus",
539 move(prop));
540 }
541 prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
542 objectPath.c_str(),
543 "org.freedesktop.DBus.Properties", "Get");
544 prop.append("xyz.openbmc_project.Object.Enable");
545 prop.append("Enabled");
546 try
547 {
548 auto result = bus.call(prop);
549 }
550 catch (const sdbusplus::exception::SdBusError& e)
551 {
552 // Treat as property unavailable
553 inventory::PropertyMap prop;
554 prop.emplace("Enabled", false);
555 interfaces.emplace("xyz.openbmc_project.Object.Enable", move(prop));
556 }
557}
558
559/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530560 * @brief Prime the Inventory
561 * Prime the inventory by populating only the location code,
562 * type interface and the inventory object for the frus
563 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530564 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530565 * @param[in] jsObject - Reference to vpd inventory json object
566 * @param[in] vpdMap - Reference to the parsed vpd map
567 *
568 * @returns Map of items in extraInterface.
569 */
570template <typename T>
571inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
572 const T& vpdMap)
573{
574 inventory::ObjectMap objects;
575
576 for (auto& itemFRUS : jsObject["frus"].items())
577 {
Alpana Kumari2f793042020-08-18 05:51:03 -0500578 // Take pre actions
579 preAction(jsObject, itemFRUS.key());
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530580 for (auto& itemEEPROM : itemFRUS.value())
581 {
582 inventory::InterfaceMap interfaces;
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530583 inventory::Object object(itemEEPROM.at("inventoryPath"));
584
Santosh Puranik50f60bf2021-05-26 17:55:06 +0530585 if ((itemFRUS.key() != systemVpdFilePath) &&
586 !itemEEPROM.value("noprime", false))
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530587 {
Alpana Kumaricfd7a752021-02-07 23:23:01 -0600588 inventory::PropertyMap presProp;
589 presProp.emplace("Present", false);
590 interfaces.emplace("xyz.openbmc_project.Inventory.Item",
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530591 presProp);
592 setOneTimeProperties(object, interfaces);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530593 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
594 {
595 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
596 {
597 inventory::PropertyMap props;
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000598 if (eI.key() == IBM_LOCATION_CODE_INF)
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530599 {
600 if constexpr (std::is_same<T, Parsed>::value)
601 {
602 for (auto& lC : eI.value().items())
603 {
604 auto propVal = expandLocationCode(
605 lC.value().get<string>(), vpdMap, true);
606
607 props.emplace(move(lC.key()),
608 move(propVal));
Santosh Puranikb0f37492021-06-21 09:42:47 +0530609 interfaces.emplace(XYZ_LOCATION_CODE_INF,
610 props);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530611 interfaces.emplace(move(eI.key()),
612 move(props));
613 }
614 }
615 }
616 else if (eI.key().find("Inventory.Item.") !=
617 string::npos)
618 {
619 interfaces.emplace(move(eI.key()), move(props));
620 }
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530621 else if (eI.key() ==
622 "xyz.openbmc_project.Inventory.Item")
623 {
624 for (auto& val : eI.value().items())
625 {
626 if (val.key() == "PrettyName")
627 {
628 presProp.emplace(val.key(),
629 val.value().get<string>());
630 }
631 }
632 // Use insert_or_assign here as we may already have
633 // inserted the present property only earlier in
634 // this function under this same interface.
635 interfaces.insert_or_assign(eI.key(),
636 move(presProp));
637 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530638 }
639 }
640 objects.emplace(move(object), move(interfaces));
641 }
642 }
643 }
644 return objects;
645}
646
Alpana Kumari65b83602020-09-01 00:24:56 -0500647/**
648 * @brief This API executes command to set environment variable
649 * And then reboot the system
650 * @param[in] key -env key to set new value
651 * @param[in] value -value to set.
652 */
653void setEnvAndReboot(const string& key, const string& value)
654{
655 // set env and reboot and break.
656 executeCmd("/sbin/fw_setenv", key, value);
Andrew Geissler280197e2020-12-08 20:51:49 -0600657 log<level::INFO>("Rebooting BMC to pick up new device tree");
Alpana Kumari65b83602020-09-01 00:24:56 -0500658 // make dbus call to reboot
659 auto bus = sdbusplus::bus::new_default_system();
660 auto method = bus.new_method_call(
661 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
662 "org.freedesktop.systemd1.Manager", "Reboot");
663 bus.call_noreply(method);
664}
665
666/*
667 * @brief This API checks for env var fitconfig.
668 * If not initialised OR updated as per the current system type,
669 * update this env var and reboot the system.
670 *
671 * @param[in] systemType IM kwd in vpd tells about which system type it is.
672 * */
673void setDevTreeEnv(const string& systemType)
674{
Alpana Kumari37e72702021-11-18 11:18:04 -0600675 // Init with default dtb
676 string newDeviceTree = "conf-aspeed-bmc-ibm-rainier-p1.dtb";
Santosh Puranike5f177a2022-01-24 20:14:46 +0530677 static const deviceTreeMap deviceTreeSystemTypeMap = {
678 {RAINIER_2U, "conf-aspeed-bmc-ibm-rainier-p1.dtb"},
679 {RAINIER_2U_V2, "conf-aspeed-bmc-ibm-rainier.dtb"},
680 {RAINIER_4U, "conf-aspeed-bmc-ibm-rainier-4u-p1.dtb"},
681 {RAINIER_4U_V2, "conf-aspeed-bmc-ibm-rainier-4u.dtb"},
682 {RAINIER_1S4U, "conf-aspeed-bmc-ibm-rainier-1s4u.dtb"},
683 {EVEREST, "conf-aspeed-bmc-ibm-everest.dtb"}};
Alpana Kumari65b83602020-09-01 00:24:56 -0500684
685 if (deviceTreeSystemTypeMap.find(systemType) !=
686 deviceTreeSystemTypeMap.end())
687 {
688 newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
689 }
Alpana Kumari37e72702021-11-18 11:18:04 -0600690 else
691 {
692 // System type not supported
Alpana Kumariab1e22c2021-11-24 11:03:38 -0600693 string err = "This System type not found/supported in dtb table " +
694 systemType +
695 ".Please check the HW and IM keywords in the system "
696 "VPD.Breaking...";
697
698 // map to hold additional data in case of logging pel
699 PelAdditionalData additionalData{};
700 additionalData.emplace("DESCRIPTION", err);
701 createPEL(additionalData, PelSeverity::WARNING,
702 errIntfForInvalidSystemType);
703 exit(-1);
Alpana Kumari37e72702021-11-18 11:18:04 -0600704 }
Alpana Kumari65b83602020-09-01 00:24:56 -0500705
706 string readVarValue;
707 bool envVarFound = false;
708
709 vector<string> output = executeCmd("/sbin/fw_printenv");
710 for (const auto& entry : output)
711 {
712 size_t pos = entry.find("=");
713 string key = entry.substr(0, pos);
714 if (key != "fitconfig")
715 {
716 continue;
717 }
718
719 envVarFound = true;
720 if (pos + 1 < entry.size())
721 {
722 readVarValue = entry.substr(pos + 1);
723 if (readVarValue.find(newDeviceTree) != string::npos)
724 {
725 // fitconfig is Updated. No action needed
726 break;
727 }
728 }
729 // set env and reboot and break.
730 setEnvAndReboot(key, newDeviceTree);
731 exit(0);
732 }
733
734 // check If env var Not found
735 if (!envVarFound)
736 {
737 setEnvAndReboot("fitconfig", newDeviceTree);
738 }
739}
740
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530741/**
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500742 * @brief API to call VPD manager to write VPD to EEPROM.
743 * @param[in] Object path.
744 * @param[in] record to be updated.
745 * @param[in] keyword to be updated.
746 * @param[in] keyword data to be updated
747 */
748void updateHardware(const string& objectName, const string& recName,
749 const string& kwdName, const Binary& data)
750{
751 try
752 {
753 auto bus = sdbusplus::bus::new_default();
754 auto properties =
755 bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
756 properties.append(
757 static_cast<sdbusplus::message::object_path>(objectName));
758 properties.append(recName);
759 properties.append(kwdName);
760 properties.append(data);
761 bus.call(properties);
762 }
Patrick Williams8be43342021-09-02 09:33:36 -0500763 catch (const sdbusplus::exception::exception& e)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500764 {
765 std::string what =
766 "VPDManager WriteKeyword api failed for inventory path " +
767 objectName;
768 what += " record " + recName;
769 what += " keyword " + kwdName;
770 what += " with bus error = " + std::string(e.what());
771
772 // map to hold additional data in case of logging pel
773 PelAdditionalData additionalData{};
774 additionalData.emplace("CALLOUT_INVENTORY_PATH", objectName);
775 additionalData.emplace("DESCRIPTION", what);
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500776 createPEL(additionalData, PelSeverity::WARNING, errIntfForBusFailure);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500777 }
778}
779
780/**
Sunny Srivastava3c244142022-01-11 08:47:04 -0600781 * @brief An api to get list of blank system VPD properties.
782 * @param[in] vpdMap - IPZ vpd map.
783 * @param[in] objectPath - Object path for the FRU.
784 * @param[out] blankPropertyList - Properties which are blank in System VPD and
785 * needs to be updated as standby.
786 */
787void getListOfBlankSystemVpd(Parsed& vpdMap, const string& objectPath,
788 std::vector<RestoredEeproms>& blankPropertyList)
789{
790 for (const auto& systemRecKwdPair : svpdKwdMap)
791 {
792 auto it = vpdMap.find(systemRecKwdPair.first);
793
794 // check if record is found in map we got by parser
795 if (it != vpdMap.end())
796 {
797 const auto& kwdListForRecord = systemRecKwdPair.second;
798 for (const auto& keyword : kwdListForRecord)
799 {
800 DbusPropertyMap& kwdValMap = it->second;
801 auto iterator = kwdValMap.find(keyword);
802
803 if (iterator != kwdValMap.end())
804 {
805 string& kwdValue = iterator->second;
806
807 // check bus data
808 const string& recordName = systemRecKwdPair.first;
809 const string& busValue = readBusProperty(
810 objectPath, ipzVpdInf + recordName, keyword);
811
812 if (busValue.find_first_not_of(' ') != string::npos)
813 {
814 if (kwdValue.find_first_not_of(' ') == string::npos)
815 {
816 // implies data is blank on EEPROM but not on cache.
817 // So EEPROM vpd update is required.
818 Binary busData(busValue.begin(), busValue.end());
819
820 blankPropertyList.push_back(std::make_tuple(
821 objectPath, recordName, keyword, busData));
822 }
823 }
824 }
825 }
826 }
827 }
828}
829
830/**
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500831 * @brief API to check if we need to restore system VPD
832 * This functionality is only applicable for IPZ VPD data.
833 * @param[in] vpdMap - IPZ vpd map
834 * @param[in] objectPath - Object path for the FRU
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500835 */
Sunny Srivastava3c244142022-01-11 08:47:04 -0600836void restoreSystemVPD(Parsed& vpdMap, const string& objectPath)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500837{
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500838 for (const auto& systemRecKwdPair : svpdKwdMap)
839 {
840 auto it = vpdMap.find(systemRecKwdPair.first);
841
842 // check if record is found in map we got by parser
843 if (it != vpdMap.end())
844 {
845 const auto& kwdListForRecord = systemRecKwdPair.second;
846 for (const auto& keyword : kwdListForRecord)
847 {
848 DbusPropertyMap& kwdValMap = it->second;
849 auto iterator = kwdValMap.find(keyword);
850
851 if (iterator != kwdValMap.end())
852 {
853 string& kwdValue = iterator->second;
854
855 // check bus data
856 const string& recordName = systemRecKwdPair.first;
857 const string& busValue = readBusProperty(
858 objectPath, ipzVpdInf + recordName, keyword);
859
860 if (busValue.find_first_not_of(' ') != string::npos)
861 {
862 if (kwdValue.find_first_not_of(' ') != string::npos)
863 {
864 // both the data are present, check for mismatch
865 if (busValue != kwdValue)
866 {
867 string errMsg = "VPD data mismatch on cache "
868 "and hardware for record: ";
869 errMsg += (*it).first;
870 errMsg += " and keyword: ";
871 errMsg += keyword;
872
873 // data mismatch
874 PelAdditionalData additionalData;
875 additionalData.emplace("CALLOUT_INVENTORY_PATH",
876 objectPath);
877
878 additionalData.emplace("DESCRIPTION", errMsg);
879
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500880 createPEL(additionalData, PelSeverity::WARNING,
881 errIntfForInvalidVPD);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500882 }
883 }
884 else
885 {
886 // implies hardware data is blank
887 // update the map
888 Binary busData(busValue.begin(), busValue.end());
889
Sunny Srivastava90a63b92021-05-26 06:30:24 -0500890 // update the map as well, so that cache data is not
891 // updated as blank while populating VPD map on Dbus
892 // in populateDBus Api
893 kwdValue = busValue;
894 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500895 }
896 else if (kwdValue.find_first_not_of(' ') == string::npos)
897 {
898 string errMsg = "VPD is blank on both cache and "
899 "hardware for record: ";
900 errMsg += (*it).first;
901 errMsg += " and keyword: ";
902 errMsg += keyword;
903 errMsg += ". SSR need to update hardware VPD.";
904
905 // both the data are blanks, log PEL
906 PelAdditionalData additionalData;
907 additionalData.emplace("CALLOUT_INVENTORY_PATH",
908 objectPath);
909
910 additionalData.emplace("DESCRIPTION", errMsg);
911
912 // log PEL TODO: Block IPL
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500913 createPEL(additionalData, PelSeverity::ERROR,
914 errIntfForBlankSystemVPD);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500915 continue;
916 }
917 }
918 }
919 }
920 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500921}
922
923/**
alpana077ce68722021-07-25 13:23:59 -0500924 * @brief This checks for is this FRU a processor
925 * And if yes, then checks for is this primary
926 *
927 * @param[in] js- vpd json to get the information about this FRU
928 * @param[in] filePath- FRU vpd
929 *
930 * @return true/false
931 */
932bool isThisPrimaryProcessor(nlohmann::json& js, const string& filePath)
933{
934 bool isProcessor = false;
935 bool isPrimary = false;
936
937 for (const auto& item : js["frus"][filePath])
938 {
939 if (item.find("extraInterfaces") != item.end())
940 {
941 for (const auto& eI : item["extraInterfaces"].items())
942 {
943 if (eI.key().find("Inventory.Item.Cpu") != string::npos)
944 {
945 isProcessor = true;
946 }
947 }
948 }
949
950 if (isProcessor)
951 {
952 string cpuType = item.value("cpuType", "");
953 if (cpuType == "primary")
954 {
955 isPrimary = true;
956 }
957 }
958 }
959
960 return (isProcessor && isPrimary);
961}
962
963/**
964 * @brief This finds DIMM vpd in vpd json and enables them by binding the device
965 * driver
966 * @param[in] js- vpd json to iterate through and take action if it is DIMM
967 */
968void doEnableAllDimms(nlohmann::json& js)
969{
970 // iterate over each fru
971 for (const auto& eachFru : js["frus"].items())
972 {
973 // skip the driver binding if eeprom already exists
974 if (fs::exists(eachFru.key()))
975 {
976 continue;
977 }
978
979 for (const auto& eachInventory : eachFru.value())
980 {
981 if (eachInventory.find("extraInterfaces") != eachInventory.end())
982 {
983 for (const auto& eI : eachInventory["extraInterfaces"].items())
984 {
985 if (eI.key().find("Inventory.Item.Dimm") != string::npos)
986 {
987 string dimmVpd = eachFru.key();
988 // fetch it from
989 // "/sys/bus/i2c/drivers/at24/414-0050/eeprom"
990
991 regex matchPatern("([0-9]+-[0-9]{4})");
992 smatch matchFound;
993 if (regex_search(dimmVpd, matchFound, matchPatern))
994 {
995 vector<string> i2cReg;
996 boost::split(i2cReg, matchFound.str(0),
997 boost::is_any_of("-"));
998
999 // remove 0s from begining
1000 const regex pattern("^0+(?!$)");
1001 for (auto& i : i2cReg)
1002 {
1003 i = regex_replace(i, pattern, "");
1004 }
1005
1006 if (i2cReg.size() == 2)
1007 {
1008 // echo 24c32 0x50 >
1009 // /sys/bus/i2c/devices/i2c-16/new_device
1010 string cmnd = "echo 24c32 0x" + i2cReg[1] +
1011 " > /sys/bus/i2c/devices/i2c-" +
1012 i2cReg[0] + "/new_device";
1013
1014 executeCmd(cmnd);
1015 }
1016 }
1017 }
1018 }
1019 }
1020 }
1021 }
1022}
1023
1024/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301025 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301026 * This method invokes all the populateInterface functions
1027 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301028 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
1029 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301030 * @param[in] js - Inventory json object
1031 * @param[in] filePath - Path of the vpd file
1032 * @param[in] preIntrStr - Interface string
1033 */
1034template <typename T>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001035static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301036{
1037 inventory::InterfaceMap interfaces;
1038 inventory::ObjectMap objects;
1039 inventory::PropertyMap prop;
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001040 string ccinFromVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301041
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301042 bool isSystemVpd = (filePath == systemVpdFilePath);
1043 if constexpr (is_same<T, Parsed>::value)
1044 {
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001045 ccinFromVpd = getKwVal(vpdMap, "VINI", "CC");
1046 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1047 ::toupper);
1048
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301049 if (isSystemVpd)
1050 {
1051 std::vector<std::string> interfaces = {motherBoardInterface};
1052 // call mapper to check for object path creation
1053 MapperResponse subTree =
1054 getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
1055 string mboardPath =
1056 js["frus"][filePath].at(0).value("inventoryPath", "");
1057
1058 // Attempt system VPD restore if we have a motherboard
1059 // object in the inventory.
1060 if ((subTree.size() != 0) &&
1061 (subTree.find(pimPath + mboardPath) != subTree.end()))
1062 {
Sunny Srivastava3c244142022-01-11 08:47:04 -06001063 restoreSystemVPD(vpdMap, mboardPath);
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301064 }
1065 else
1066 {
1067 log<level::ERR>("No object path found");
1068 }
1069 }
alpana077ce68722021-07-25 13:23:59 -05001070 else
1071 {
1072 // check if it is processor vpd.
1073 auto isPrimaryCpu = isThisPrimaryProcessor(js, filePath);
1074
1075 if (isPrimaryCpu)
1076 {
1077 auto ddVersion = getKwVal(vpdMap, "CRP0", "DD");
1078
1079 auto chipVersion = atoi(ddVersion.substr(1, 2).c_str());
1080
1081 if (chipVersion >= 2)
1082 {
1083 doEnableAllDimms(js);
1084 }
1085 }
1086 }
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301087 }
1088
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001089 if (isSystemVpd)
1090 {
1091 string systemJsonName{};
1092 if constexpr (is_same<T, Parsed>::value)
1093 {
1094 // pick the right system json
1095 systemJsonName = getSystemsJson(vpdMap);
1096 }
1097
1098 fs::path target = systemJsonName;
1099 fs::path link = INVENTORY_JSON_SYM_LINK;
1100
1101 // Create the directory for hosting the symlink
1102 fs::create_directories(VPD_FILES_PATH);
1103 // unlink the symlink previously created (if any)
1104 remove(INVENTORY_JSON_SYM_LINK);
1105 // create a new symlink based on the system
1106 fs::create_symlink(target, link);
1107
1108 // Reloading the json
1109 ifstream inventoryJson(link);
1110 js = json::parse(inventoryJson);
1111 inventoryJson.close();
1112 }
1113
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301114 for (const auto& item : js["frus"][filePath])
1115 {
1116 const auto& objectPath = item["inventoryPath"];
1117 sdbusplus::message::object_path object(objectPath);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001118
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001119 vector<string> ccinList;
1120 if (item.find("ccin") != item.end())
1121 {
1122 for (const auto& cc : item["ccin"])
1123 {
1124 string ccin = cc;
1125 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1126 ccinList.push_back(ccin);
1127 }
1128 }
1129
1130 if (!ccinFromVpd.empty() && !ccinList.empty() &&
1131 (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1132 ccinList.end()))
1133 {
1134 continue;
1135 }
1136
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001137 if ((isSystemVpd) || (item.value("noprime", false)))
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301138 {
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001139
1140 // Populate one time properties for the system VPD and its sub-frus
1141 // and for other non-primeable frus.
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301142 // For the remaining FRUs, this will get handled as a part of
1143 // priming the inventory.
1144 setOneTimeProperties(objectPath, interfaces);
1145 }
1146
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301147 // Populate the VPD keywords and the common interfaces only if we
1148 // are asked to inherit that data from the VPD, else only add the
1149 // extraInterfaces.
1150 if (item.value("inherit", true))
1151 {
Alpana Kumari58e22142020-05-05 00:22:12 -05001152 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301153 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301154 // Each record in the VPD becomes an interface and all
1155 // keyword within the record are properties under that
1156 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301157 for (const auto& record : vpdMap)
1158 {
1159 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001160 record.second, ipzVpdInf + record.first, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301161 }
1162 }
Alpana Kumari58e22142020-05-05 00:22:12 -05001163 else if constexpr (is_same<T, KeywordVpdMap>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301164 {
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001165 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301166 }
Santosh Puranik88edeb62020-03-02 12:00:09 +05301167 if (js.find("commonInterfaces") != js.end())
1168 {
1169 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
1170 isSystemVpd);
1171 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301172 }
Santosh Puranik0859eb62020-03-16 02:56:29 -05001173 else
1174 {
1175 // Check if we have been asked to inherit specific record(s)
Alpana Kumari58e22142020-05-05 00:22:12 -05001176 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik0859eb62020-03-16 02:56:29 -05001177 {
1178 if (item.find("copyRecords") != item.end())
1179 {
1180 for (const auto& record : item["copyRecords"])
1181 {
1182 const string& recordName = record;
1183 if (vpdMap.find(recordName) != vpdMap.end())
1184 {
1185 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001186 vpdMap.at(recordName), ipzVpdInf + recordName,
Santosh Puranik0859eb62020-03-16 02:56:29 -05001187 interfaces);
1188 }
1189 }
1190 }
1191 }
1192 }
Santosh Puranik32c46502022-02-10 08:55:07 +05301193 // Populate interfaces and properties that are common to every FRU
1194 // and additional interface that might be defined on a per-FRU
1195 // basis.
1196 if (item.find("extraInterfaces") != item.end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301197 {
Santosh Puranik32c46502022-02-10 08:55:07 +05301198 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
1199 isSystemVpd);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301200 }
1201 objects.emplace(move(object), move(interfaces));
1202 }
1203
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301204 if (isSystemVpd)
1205 {
1206 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
1207 objects.insert(primeObject.begin(), primeObject.end());
Alpana Kumari65b83602020-09-01 00:24:56 -05001208
Alpana Kumarif05effd2021-04-07 07:32:53 -05001209 // set the U-boot environment variable for device-tree
1210 if constexpr (is_same<T, Parsed>::value)
1211 {
Santosh Puranike5f177a2022-01-24 20:14:46 +05301212 setDevTreeEnv(fs::path(getSystemsJson(vpdMap)).filename());
Alpana Kumarif05effd2021-04-07 07:32:53 -05001213 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301214 }
1215
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301216 // Notify PIM
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05001217 common::utility::callPIM(move(objects));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301218}
1219
1220int main(int argc, char** argv)
1221{
1222 int rc = 0;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001223 json js{};
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001224 Binary vpdVector{};
1225 string file{};
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001226 // map to hold additional data in case of logging pel
1227 PelAdditionalData additionalData{};
1228
1229 // this is needed to hold base fru inventory path in case there is ECC or
1230 // vpd exception while parsing the file
1231 std::string baseFruInventoryPath = {};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301232
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001233 // severity for PEL
1234 PelSeverity pelSeverity = PelSeverity::WARNING;
1235
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301236 try
1237 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301238 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
1239 "in DBUS"};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301240
1241 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
Alpana Kumari2f793042020-08-18 05:51:03 -05001242 ->required();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301243
1244 CLI11_PARSE(app, argc, argv);
1245
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001246 // PEL severity should be ERROR in case of any system VPD failure
1247 if (file == systemVpdFilePath)
1248 {
1249 pelSeverity = PelSeverity::ERROR;
1250 }
1251
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301252 auto jsonToParse = INVENTORY_JSON_DEFAULT;
1253
1254 // If the symlink exists, it means it has been setup for us, switch the
1255 // path
1256 if (fs::exists(INVENTORY_JSON_SYM_LINK))
1257 {
1258 jsonToParse = INVENTORY_JSON_SYM_LINK;
1259 }
1260
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301261 // Make sure that the file path we get is for a supported EEPROM
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301262 ifstream inventoryJson(jsonToParse);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001263 if (!inventoryJson)
1264 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001265 throw(VpdJsonException("Failed to access Json path", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001266 }
1267
1268 try
1269 {
1270 js = json::parse(inventoryJson);
1271 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001272 catch (const json::parse_error& ex)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001273 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001274 throw(VpdJsonException("Json parsing failed", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001275 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301276
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301277 // Do we have the mandatory "frus" section?
1278 if (js.find("frus") == js.end())
1279 {
1280 throw(VpdJsonException("FRUs section not found in JSON",
1281 jsonToParse));
1282 }
1283
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301284 // Check if it's a udev path - patterned as(/ahb/ahb:apb/ahb:apb:bus@)
1285 if (file.find("/ahb:apb") != string::npos)
1286 {
1287 // Translate udev path to a generic /sys/bus/.. file path.
1288 udevToGenericPath(file);
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301289
1290 if ((js["frus"].find(file) != js["frus"].end()) &&
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301291 (file == systemVpdFilePath))
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301292 {
Sunny Srivastava3c244142022-01-11 08:47:04 -06001293 // We need manager service active to process restoring of
1294 // system VPD on hardware. So in case any system restore is
1295 // required, update hardware in the second trigger of parser
1296 // code for system vpd file path.
1297
1298 std::vector<std::string> interfaces{motherBoardInterface};
1299
1300 // call mapper to check for object path creation
1301 MapperResponse subTree =
1302 getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
1303 string mboardPath =
1304 js["frus"][file].at(0).value("inventoryPath", "");
1305
1306 // Attempt system VPD restore if we have a motherboard
1307 // object in the inventory.
1308 if ((subTree.size() != 0) &&
1309 (subTree.find(pimPath + mboardPath) != subTree.end()))
1310 {
1311 vpdVector = getVpdDataInVector(js, file);
1312 ParserInterface* parser =
1313 ParserFactory::getParser(vpdVector);
1314 variant<KeywordVpdMap, Store> parseResult;
1315 parseResult = parser->parse();
1316
1317 if (auto pVal = get_if<Store>(&parseResult))
1318 {
1319 // map to hold all the keywords whose value is blank and
1320 // needs to be updated at standby.
1321 vector<RestoredEeproms> blankSystemVpdProperties{};
1322 getListOfBlankSystemVpd(pVal->getVpdMap(), mboardPath,
1323 blankSystemVpdProperties);
1324
1325 // if system VPD restore is required, update the
1326 // EEPROM
1327 for (const auto& item : blankSystemVpdProperties)
1328 {
1329 updateHardware(get<0>(item), get<1>(item),
1330 get<2>(item), get<3>(item));
1331 }
1332 }
1333 else
1334 {
1335 std::cout << "Not a valid format to restore system VPD"
1336 << std::endl;
1337 }
1338 // release the parser object
1339 ParserFactory::freeParser(parser);
1340 }
1341 else
1342 {
1343 log<level::ERR>("No object path found");
1344 }
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301345 return 0;
1346 }
1347 }
1348
1349 if (file.empty())
1350 {
1351 cerr << "The EEPROM path <" << file << "> is not valid.";
1352 return 0;
1353 }
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301354 if (js["frus"].find(file) == js["frus"].end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301355 {
Santosh Puranik88edeb62020-03-02 12:00:09 +05301356 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301357 }
1358
Alpana Kumari2f793042020-08-18 05:51:03 -05001359 if (!fs::exists(file))
1360 {
1361 cout << "Device path: " << file
1362 << " does not exist. Spurious udev event? Exiting." << endl;
1363 return 0;
1364 }
1365
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001366 baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
Santosh Puranik85893752020-11-10 21:31:43 +05301367 // Check if we can read the VPD file based on the power state
Santosh Puranik27a5e952021-10-07 22:08:01 -05001368 // We skip reading VPD when the power is ON in two scenarios:
1369 // 1) The eeprom we are trying to read is that of the system VPD
1370 // 2) The JSON tells us that the FRU EEPROM cannot be read when
1371 // we are powered ON.
1372 if (js["frus"][file].at(0).value("powerOffOnly", false) ||
1373 (file == systemVpdFilePath))
Santosh Puranik85893752020-11-10 21:31:43 +05301374 {
1375 if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
1376 getPowerState())
1377 {
1378 cout << "This VPD cannot be read when power is ON" << endl;
1379 return 0;
1380 }
1381 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001382
Alpana Kumari2f793042020-08-18 05:51:03 -05001383 try
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301384 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001385 vpdVector = getVpdDataInVector(js, file);
PriyangaRamasamy33c61c22021-04-06 11:15:57 -05001386 ParserInterface* parser = ParserFactory::getParser(vpdVector);
Alpana Kumari2f793042020-08-18 05:51:03 -05001387 variant<KeywordVpdMap, Store> parseResult;
1388 parseResult = parser->parse();
SunnySrivastava19849a195542020-09-07 06:04:50 -05001389
Alpana Kumari2f793042020-08-18 05:51:03 -05001390 if (auto pVal = get_if<Store>(&parseResult))
1391 {
1392 populateDbus(pVal->getVpdMap(), js, file);
1393 }
1394 else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
1395 {
1396 populateDbus(*pVal, js, file);
1397 }
1398
1399 // release the parser object
1400 ParserFactory::freeParser(parser);
1401 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001402 catch (const exception& e)
Alpana Kumari2f793042020-08-18 05:51:03 -05001403 {
1404 postFailAction(js, file);
PriyangaRamasamya504c3e2020-12-06 12:14:52 -06001405 throw;
Alpana Kumari2f793042020-08-18 05:51:03 -05001406 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301407 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001408 catch (const VpdJsonException& ex)
1409 {
1410 additionalData.emplace("JSON_PATH", ex.getJsonPath());
1411 additionalData.emplace("DESCRIPTION", ex.what());
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001412 createPEL(additionalData, pelSeverity, errIntfForJsonFailure);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001413
1414 cerr << ex.what() << "\n";
1415 rc = -1;
1416 }
1417 catch (const VpdEccException& ex)
1418 {
1419 additionalData.emplace("DESCRIPTION", "ECC check failed");
1420 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1421 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001422 createPEL(additionalData, pelSeverity, errIntfForEccCheckFail);
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001423 dumpBadVpd(file, vpdVector);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001424 cerr << ex.what() << "\n";
1425 rc = -1;
1426 }
1427 catch (const VpdDataException& ex)
1428 {
1429 additionalData.emplace("DESCRIPTION", "Invalid VPD data");
1430 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1431 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001432 createPEL(additionalData, pelSeverity, errIntfForInvalidVPD);
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001433 dumpBadVpd(file, vpdVector);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001434 cerr << ex.what() << "\n";
1435 rc = -1;
1436 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001437 catch (const exception& e)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301438 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001439 dumpBadVpd(file, vpdVector);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301440 cerr << e.what() << "\n";
1441 rc = -1;
1442 }
1443
1444 return rc;
Alpana Kumari37e72702021-11-18 11:18:04 -06001445}