blob: b09ed795a9e51c44aa9d2e6f26ece4ff49af1f9a [file] [log] [blame]
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301#include "config.h"
2
3#include "defines.hpp"
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05004#include "ipz_parser.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05305#include "keyword_vpd_parser.hpp"
Alpana Kumaria00936f2020-04-14 07:15:46 -05006#include "memory_vpd_parser.hpp"
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05007#include "parser_factory.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05308#include "utils.hpp"
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05009#include "vpd_exceptions.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053010
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -050011#include <ctype.h>
12
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053013#include <CLI/CLI.hpp>
Santosh Puranik88edeb62020-03-02 12:00:09 +053014#include <algorithm>
Alpana Kumari65b83602020-09-01 00:24:56 -050015#include <cstdarg>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053016#include <exception>
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053017#include <filesystem>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053018#include <fstream>
Alpana Kumari2f793042020-08-18 05:51:03 -050019#include <gpiod.hpp>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053020#include <iostream>
21#include <iterator>
22#include <nlohmann/json.hpp>
Andrew Geissler280197e2020-12-08 20:51:49 -060023#include <phosphor-logging/log.hpp>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053024
25using namespace std;
26using namespace openpower::vpd;
27using namespace CLI;
28using namespace vpd::keyword::parser;
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053029using namespace openpower::vpd::constants;
30namespace fs = filesystem;
31using json = nlohmann::json;
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050032using namespace openpower::vpd::parser::factory;
SunnySrivastava1984945a02d2020-05-06 01:55:41 -050033using namespace openpower::vpd::inventory;
Alpana Kumaria00936f2020-04-14 07:15:46 -050034using namespace openpower::vpd::memory::parser;
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050035using namespace openpower::vpd::parser::interface;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -050036using namespace openpower::vpd::exceptions;
Andrew Geissler280197e2020-12-08 20:51:49 -060037using namespace phosphor::logging;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053038
Alpana Kumari65b83602020-09-01 00:24:56 -050039static const deviceTreeMap deviceTreeSystemTypeMap = {
Santosh Puranik4641bff2020-11-30 20:26:44 +053040 {RAINIER_2U, "conf@aspeed-bmc-ibm-rainier.dtb"},
41 {RAINIER_4U, "conf@aspeed-bmc-ibm-rainier-4u.dtb"},
42 {EVEREST, "conf@aspeed-bmc-ibm-everest.dtb"}};
Alpana Kumari65b83602020-09-01 00:24:56 -050043
Santosh Puranik88edeb62020-03-02 12:00:09 +053044/**
Santosh Puranik85893752020-11-10 21:31:43 +053045 * @brief Returns the power state for chassis0
46 */
47static auto getPowerState()
48{
49 // TODO: How do we handle multiple chassis?
50 string powerState{};
51 auto bus = sdbusplus::bus::new_default();
52 auto properties =
53 bus.new_method_call("xyz.openbmc_project.State.Chassis",
54 "/xyz/openbmc_project/state/chassis0",
55 "org.freedesktop.DBus.Properties", "Get");
56 properties.append("xyz.openbmc_project.State.Chassis");
57 properties.append("CurrentPowerState");
58 auto result = bus.call(properties);
59 if (!result.is_method_error())
60 {
61 variant<string> val;
62 result.read(val);
63 if (auto pVal = get_if<string>(&val))
64 {
65 powerState = *pVal;
66 }
67 }
68 cout << "Power state is: " << powerState << endl;
69 return powerState;
70}
71
72/**
Santosh Puranik88edeb62020-03-02 12:00:09 +053073 * @brief Expands location codes
74 */
75static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
76 bool isSystemVpd)
77{
78 auto expanded{unexpanded};
79 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
80 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
81 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
82 size_t idx = expanded.find("fcs");
83 try
84 {
85 if (idx != string::npos)
86 {
87 string fc{};
88 string se{};
89 if (isSystemVpd)
90 {
91 const auto& fcData = vpdMap.at("VCEN").at("FC");
92 const auto& seData = vpdMap.at("VCEN").at("SE");
93 fc = string(fcData.data(), fcData.size());
94 se = string(seData.data(), seData.size());
95 }
96 else
97 {
98 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
99 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
100 }
101
102 // TODO: See if ND1 can be placed in the JSON
103 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND1." + se);
104 }
105 else
106 {
107 idx = expanded.find("mts");
108 if (idx != string::npos)
109 {
110 string mt{};
111 string se{};
112 if (isSystemVpd)
113 {
114 const auto& mtData = vpdMap.at("VSYS").at("TM");
115 const auto& seData = vpdMap.at("VSYS").at("SE");
116 mt = string(mtData.data(), mtData.size());
117 se = string(seData.data(), seData.size());
118 }
119 else
120 {
121 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
122 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
123 }
124
125 replace(mt.begin(), mt.end(), '-', '.');
126 expanded.replace(idx, 3, mt + "." + se);
127 }
128 }
129 }
Alpana Kumari58e22142020-05-05 00:22:12 -0500130 catch (exception& e)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530131 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500132 cerr << "Failed to expand location code with exception: " << e.what()
133 << "\n";
Santosh Puranik88edeb62020-03-02 12:00:09 +0530134 }
135 return expanded;
136}
Alpana Kumari2f793042020-08-18 05:51:03 -0500137
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530138/**
139 * @brief Populate FRU specific interfaces.
140 *
141 * This is a common method which handles both
142 * ipz and keyword specific interfaces thus,
143 * reducing the code redundancy.
144 * @param[in] map - Reference to the innermost keyword-value map.
145 * @param[in] preIntrStr - Reference to the interface string.
146 * @param[out] interfaces - Reference to interface map.
147 */
148template <typename T>
149static void populateFruSpecificInterfaces(const T& map,
150 const string& preIntrStr,
151 inventory::InterfaceMap& interfaces)
152{
153 inventory::PropertyMap prop;
154
155 for (const auto& kwVal : map)
156 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500157 vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530158
159 auto kw = kwVal.first;
160
161 if (kw[0] == '#')
162 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500163 kw = string("PD_") + kw[1];
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530164 }
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500165 else if (isdigit(kw[0]))
166 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500167 kw = string("N_") + kw;
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500168 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530169 prop.emplace(move(kw), move(vec));
170 }
171
172 interfaces.emplace(preIntrStr, move(prop));
173}
174
175/**
176 * @brief Populate Interfaces.
177 *
178 * This method populates common and extra interfaces to dbus.
179 * @param[in] js - json object
180 * @param[out] interfaces - Reference to interface map
181 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530182 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530183 */
184template <typename T>
185static void populateInterfaces(const nlohmann::json& js,
186 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530187 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530188{
189 for (const auto& ifs : js.items())
190 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530191 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530192 inventory::PropertyMap props;
193
194 for (const auto& itr : ifs.value().items())
195 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530196 const string& busProp = itr.key();
197
Alpana Kumari31970de2020-02-17 06:49:57 -0600198 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530199 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530200 props.emplace(busProp, itr.value().get<bool>());
201 }
202 else if (itr.value().is_string())
203 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500204 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530205 {
206 if (busProp == "LocationCode" &&
207 inf == "com.ibm.ipzvpd.Location")
208 {
209 auto prop = expandLocationCode(
210 itr.value().get<string>(), vpdMap, isSystemVpd);
211 props.emplace(busProp, prop);
212 }
213 else
214 {
215 props.emplace(busProp, itr.value().get<string>());
216 }
217 }
218 else
219 {
220 props.emplace(busProp, itr.value().get<string>());
221 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530222 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600223 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530224 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600225 const string& rec = itr.value().value("recordName", "");
226 const string& kw = itr.value().value("keywordName", "");
227 const string& encoding = itr.value().value("encoding", "");
228
Alpana Kumari58e22142020-05-05 00:22:12 -0500229 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530230 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530231 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
232 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600233 {
234 auto encoded =
235 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530236 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600237 }
238 }
Alpana Kumari58e22142020-05-05 00:22:12 -0500239 else if constexpr (is_same<T, KeywordVpdMap>::value)
Alpana Kumari31970de2020-02-17 06:49:57 -0600240 {
241 if (!kw.empty() && vpdMap.count(kw))
242 {
243 auto prop =
244 string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
245 auto encoded = encodeKeyword(prop, encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530246 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600247 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530248 }
249 }
250 }
251 interfaces.emplace(inf, move(props));
252 }
253}
254
Alpana Kumari2f793042020-08-18 05:51:03 -0500255static Binary getVpdDataInVector(const nlohmann::json& js, const string& file)
Alpana Kumari58e22142020-05-05 00:22:12 -0500256{
257 uint32_t offset = 0;
258 // check if offset present?
259 for (const auto& item : js["frus"][file])
260 {
261 if (item.find("offset") != item.end())
262 {
263 offset = item["offset"];
264 }
265 }
266
267 // TODO: Figure out a better way to get max possible VPD size.
268 Binary vpdVector;
269 vpdVector.resize(65504);
270 ifstream vpdFile;
271 vpdFile.open(file, ios::binary);
272
273 vpdFile.seekg(offset, ios_base::cur);
274 vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), 65504);
275 vpdVector.resize(vpdFile.gcount());
276
277 return vpdVector;
278}
279
Alpana Kumari2f793042020-08-18 05:51:03 -0500280/* It does nothing. Just an empty function to return null
281 * at the end of variadic template args
282 */
283static string getCommand()
284{
285 return "";
286}
287
288/* This function to arrange all arguments to make command
289 */
290template <typename T, typename... Types>
291static string getCommand(T arg1, Types... args)
292{
293 string cmd = " " + arg1 + getCommand(args...);
294
295 return cmd;
296}
297
298/* This API takes arguments and run that command
299 * returns output of that command
300 */
301template <typename T, typename... Types>
302static vector<string> executeCmd(T&& path, Types... args)
303{
304 vector<string> stdOutput;
305 array<char, 128> buffer;
306
307 string cmd = path + getCommand(args...);
308
309 unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
310 if (!pipe)
311 {
312 throw runtime_error("popen() failed!");
313 }
314 while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
315 {
316 stdOutput.emplace_back(buffer.data());
317 }
318
319 return stdOutput;
320}
321
322/** This API will be called at the end of VPD collection to perform any post
323 * actions.
324 *
325 * @param[in] json - json object
326 * @param[in] file - eeprom file path
327 */
328static void postFailAction(const nlohmann::json& json, const string& file)
329{
330 if ((json["frus"][file].at(0)).find("postActionFail") ==
331 json["frus"][file].at(0).end())
332 {
333 return;
334 }
335
336 uint8_t pinValue = 0;
337 string pinName;
338
339 for (const auto& postAction :
340 (json["frus"][file].at(0))["postActionFail"].items())
341 {
342 if (postAction.key() == "pin")
343 {
344 pinName = postAction.value();
345 }
346 else if (postAction.key() == "value")
347 {
348 // Get the value to set
349 pinValue = postAction.value();
350 }
351 }
352
353 cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
354
355 try
356 {
357 gpiod::line outputLine = gpiod::find_line(pinName);
358
359 if (!outputLine)
360 {
361 cout << "Couldn't find output line:" << pinName
362 << " on GPIO. Skipping...\n";
363
364 return;
365 }
366 outputLine.request(
367 {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
368 pinValue);
369 }
370 catch (system_error&)
371 {
372 cerr << "Failed to set post-action GPIO" << endl;
373 }
374}
375
376/** Performs any pre-action needed to get the FRU setup for collection.
377 *
378 * @param[in] json - json object
379 * @param[in] file - eeprom file path
380 */
381static void preAction(const nlohmann::json& json, const string& file)
382{
383 if ((json["frus"][file].at(0)).find("preAction") ==
384 json["frus"][file].at(0).end())
385 {
386 return;
387 }
388
389 uint8_t pinValue = 0;
390 string pinName;
391
392 for (const auto& postAction :
393 (json["frus"][file].at(0))["preAction"].items())
394 {
395 if (postAction.key() == "pin")
396 {
397 pinName = postAction.value();
398 }
399 else if (postAction.key() == "value")
400 {
401 // Get the value to set
402 pinValue = postAction.value();
403 }
404 }
405
406 cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
407 try
408 {
409 gpiod::line outputLine = gpiod::find_line(pinName);
410
411 if (!outputLine)
412 {
413 cout << "Couldn't find output line:" << pinName
414 << " on GPIO. Skipping...\n";
415
416 return;
417 }
418 outputLine.request(
419 {"FRU pre-action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
420 pinValue);
421 }
422 catch (system_error&)
423 {
424 cerr << "Failed to set pre-action GPIO" << endl;
425 return;
426 }
427
428 // Now bind the device
429 string bind = json["frus"][file].at(0).value("bind", "");
430 cout << "Binding device " << bind << endl;
431 string bindCmd = string("echo \"") + bind +
432 string("\" > /sys/bus/i2c/drivers/at24/bind");
433 cout << bindCmd << endl;
434 executeCmd(bindCmd);
435
436 // Check if device showed up (test for file)
437 if (!fs::exists(file))
438 {
439 cout << "EEPROM " << file << " does not exist. Take failure action"
440 << endl;
441 // If not, then take failure postAction
442 postFailAction(json, file);
443 }
444}
445
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530446/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530447 * @brief Prime the Inventory
448 * Prime the inventory by populating only the location code,
449 * type interface and the inventory object for the frus
450 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530451 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530452 * @param[in] jsObject - Reference to vpd inventory json object
453 * @param[in] vpdMap - Reference to the parsed vpd map
454 *
455 * @returns Map of items in extraInterface.
456 */
457template <typename T>
458inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
459 const T& vpdMap)
460{
461 inventory::ObjectMap objects;
462
463 for (auto& itemFRUS : jsObject["frus"].items())
464 {
Alpana Kumari2f793042020-08-18 05:51:03 -0500465 // Take pre actions
466 preAction(jsObject, itemFRUS.key());
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530467 for (auto& itemEEPROM : itemFRUS.value())
468 {
469 inventory::InterfaceMap interfaces;
470 auto isSystemVpd = itemEEPROM.value("isSystemVpd", false);
471 inventory::Object object(itemEEPROM.at("inventoryPath"));
472
473 if (!isSystemVpd && !itemEEPROM.value("noprime", false))
474 {
475 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
476 {
477 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
478 {
479 inventory::PropertyMap props;
480 if (eI.key() ==
481 openpower::vpd::constants::LOCATION_CODE_INF)
482 {
483 if constexpr (std::is_same<T, Parsed>::value)
484 {
485 for (auto& lC : eI.value().items())
486 {
487 auto propVal = expandLocationCode(
488 lC.value().get<string>(), vpdMap, true);
489
490 props.emplace(move(lC.key()),
491 move(propVal));
492 interfaces.emplace(move(eI.key()),
493 move(props));
494 }
495 }
496 }
497 else if (eI.key().find("Inventory.Item.") !=
498 string::npos)
499 {
500 interfaces.emplace(move(eI.key()), move(props));
501 }
502 }
503 }
504 objects.emplace(move(object), move(interfaces));
505 }
506 }
507 }
508 return objects;
509}
510
Alpana Kumari65b83602020-09-01 00:24:56 -0500511/**
512 * @brief This API executes command to set environment variable
513 * And then reboot the system
514 * @param[in] key -env key to set new value
515 * @param[in] value -value to set.
516 */
517void setEnvAndReboot(const string& key, const string& value)
518{
519 // set env and reboot and break.
520 executeCmd("/sbin/fw_setenv", key, value);
Andrew Geissler280197e2020-12-08 20:51:49 -0600521 log<level::INFO>("Rebooting BMC to pick up new device tree");
Alpana Kumari65b83602020-09-01 00:24:56 -0500522 // make dbus call to reboot
523 auto bus = sdbusplus::bus::new_default_system();
524 auto method = bus.new_method_call(
525 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
526 "org.freedesktop.systemd1.Manager", "Reboot");
527 bus.call_noreply(method);
528}
529
530/*
531 * @brief This API checks for env var fitconfig.
532 * If not initialised OR updated as per the current system type,
533 * update this env var and reboot the system.
534 *
535 * @param[in] systemType IM kwd in vpd tells about which system type it is.
536 * */
537void setDevTreeEnv(const string& systemType)
538{
539 string newDeviceTree;
540
541 if (deviceTreeSystemTypeMap.find(systemType) !=
542 deviceTreeSystemTypeMap.end())
543 {
544 newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
545 }
546
547 string readVarValue;
548 bool envVarFound = false;
549
550 vector<string> output = executeCmd("/sbin/fw_printenv");
551 for (const auto& entry : output)
552 {
553 size_t pos = entry.find("=");
554 string key = entry.substr(0, pos);
555 if (key != "fitconfig")
556 {
557 continue;
558 }
559
560 envVarFound = true;
561 if (pos + 1 < entry.size())
562 {
563 readVarValue = entry.substr(pos + 1);
564 if (readVarValue.find(newDeviceTree) != string::npos)
565 {
566 // fitconfig is Updated. No action needed
567 break;
568 }
569 }
570 // set env and reboot and break.
571 setEnvAndReboot(key, newDeviceTree);
572 exit(0);
573 }
574
575 // check If env var Not found
576 if (!envVarFound)
577 {
578 setEnvAndReboot("fitconfig", newDeviceTree);
579 }
580}
581
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530582/**
583 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530584 * This method invokes all the populateInterface functions
585 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530586 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
587 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530588 * @param[in] js - Inventory json object
589 * @param[in] filePath - Path of the vpd file
590 * @param[in] preIntrStr - Interface string
591 */
592template <typename T>
593static void populateDbus(const T& vpdMap, nlohmann::json& js,
Alpana Kumari65b83602020-09-01 00:24:56 -0500594 const string& filePath)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530595{
596 inventory::InterfaceMap interfaces;
597 inventory::ObjectMap objects;
598 inventory::PropertyMap prop;
599
SunnySrivastava1984e12b1812020-05-26 02:23:11 -0500600 bool isSystemVpd = false;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530601 for (const auto& item : js["frus"][filePath])
602 {
603 const auto& objectPath = item["inventoryPath"];
604 sdbusplus::message::object_path object(objectPath);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530605 isSystemVpd = item.value("isSystemVpd", false);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530606 // Populate the VPD keywords and the common interfaces only if we
607 // are asked to inherit that data from the VPD, else only add the
608 // extraInterfaces.
609 if (item.value("inherit", true))
610 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500611 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530612 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530613 // Each record in the VPD becomes an interface and all
614 // keyword within the record are properties under that
615 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530616 for (const auto& record : vpdMap)
617 {
618 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -0500619 record.second, ipzVpdInf + record.first, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530620 }
621 }
Alpana Kumari58e22142020-05-05 00:22:12 -0500622 else if constexpr (is_same<T, KeywordVpdMap>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530623 {
SunnySrivastava1984e12b1812020-05-26 02:23:11 -0500624 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530625 }
Santosh Puranik88edeb62020-03-02 12:00:09 +0530626 if (js.find("commonInterfaces") != js.end())
627 {
628 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
629 isSystemVpd);
630 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530631 }
Santosh Puranik0859eb62020-03-16 02:56:29 -0500632 else
633 {
634 // Check if we have been asked to inherit specific record(s)
Alpana Kumari58e22142020-05-05 00:22:12 -0500635 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik0859eb62020-03-16 02:56:29 -0500636 {
637 if (item.find("copyRecords") != item.end())
638 {
639 for (const auto& record : item["copyRecords"])
640 {
641 const string& recordName = record;
642 if (vpdMap.find(recordName) != vpdMap.end())
643 {
644 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -0500645 vpdMap.at(recordName), ipzVpdInf + recordName,
Santosh Puranik0859eb62020-03-16 02:56:29 -0500646 interfaces);
647 }
648 }
649 }
650 }
651 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530652
Alpana Kumari58e22142020-05-05 00:22:12 -0500653 if (item.value("inheritEI", true))
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530654 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500655 // Populate interfaces and properties that are common to every FRU
656 // and additional interface that might be defined on a per-FRU
657 // basis.
658 if (item.find("extraInterfaces") != item.end())
659 {
660 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
661 isSystemVpd);
662 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530663 }
664 objects.emplace(move(object), move(interfaces));
665 }
666
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530667 if (isSystemVpd)
668 {
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +0530669 vector<uint8_t> imVal;
670 if constexpr (is_same<T, Parsed>::value)
671 {
672 auto property = vpdMap.find("VSBP");
673 if (property != vpdMap.end())
674 {
675 auto value = (property->second).find("IM");
676 if (value != (property->second).end())
677 {
678 copy(value->second.begin(), value->second.end(),
679 back_inserter(imVal));
680 }
681 }
682 }
683
684 fs::path target;
685 fs::path link = INVENTORY_JSON_SYM_LINK;
686
687 ostringstream oss;
688 for (auto& i : imVal)
689 {
690 oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
691 }
692 string imValStr = oss.str();
693
Alpana Kumari65b83602020-09-01 00:24:56 -0500694 if (imValStr == RAINIER_4U) // 4U
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +0530695 {
696 target = INVENTORY_JSON_4U;
697 }
Alpana Kumari65b83602020-09-01 00:24:56 -0500698 else if (imValStr == RAINIER_2U) // 2U
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +0530699 {
700 target = INVENTORY_JSON_2U;
701 }
Santosh Puranik4641bff2020-11-30 20:26:44 +0530702 else if (imValStr == EVEREST)
703 {
704 target = INVENTORY_JSON_EVEREST;
705 }
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +0530706
Santosh Puranik0246a4d2020-11-04 16:57:39 +0530707 // Create the directory for hosting the symlink
708 fs::create_directories(VPD_FILES_PATH);
709 // unlink the symlink previously created (if any)
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +0530710 remove(INVENTORY_JSON_SYM_LINK);
711 // create a new symlink based on the system
712 fs::create_symlink(target, link);
713
714 // Reloading the json
715 ifstream inventoryJson(link);
716 auto js = json::parse(inventoryJson);
717 inventoryJson.close();
718
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530719 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
720 objects.insert(primeObject.begin(), primeObject.end());
Alpana Kumari65b83602020-09-01 00:24:56 -0500721
722 // set the U-boot environment variable for device-tree
723 setDevTreeEnv(imValStr);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530724 }
725
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530726 // Notify PIM
727 inventory::callPIM(move(objects));
728}
729
730int main(int argc, char** argv)
731{
732 int rc = 0;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500733 string file{};
734 json js{};
735
736 // map to hold additional data in case of logging pel
737 PelAdditionalData additionalData{};
738
739 // this is needed to hold base fru inventory path in case there is ECC or
740 // vpd exception while parsing the file
741 std::string baseFruInventoryPath = {};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530742
743 try
744 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530745 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
746 "in DBUS"};
747 string file{};
748
749 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
Alpana Kumari2f793042020-08-18 05:51:03 -0500750 ->required();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530751
752 CLI11_PARSE(app, argc, argv);
753
Santosh Puranik0246a4d2020-11-04 16:57:39 +0530754 auto jsonToParse = INVENTORY_JSON_DEFAULT;
755
756 // If the symlink exists, it means it has been setup for us, switch the
757 // path
758 if (fs::exists(INVENTORY_JSON_SYM_LINK))
759 {
760 jsonToParse = INVENTORY_JSON_SYM_LINK;
761 }
762
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530763 // Make sure that the file path we get is for a supported EEPROM
Santosh Puranik0246a4d2020-11-04 16:57:39 +0530764 ifstream inventoryJson(jsonToParse);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500765 if (!inventoryJson)
766 {
767 throw(
768 (VpdJsonException("Failed to access Json path", jsonToParse)));
769 }
770
771 try
772 {
773 js = json::parse(inventoryJson);
774 }
775 catch (json::parse_error& ex)
776 {
777 throw((VpdJsonException("Json parsing failed", jsonToParse)));
778 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530779
780 if ((js.find("frus") == js.end()) ||
781 (js["frus"].find(file) == js["frus"].end()))
782 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500783 cout << "Device path not in JSON, ignoring" << endl;
Santosh Puranik88edeb62020-03-02 12:00:09 +0530784 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530785 }
786
Alpana Kumari2f793042020-08-18 05:51:03 -0500787 if (!fs::exists(file))
788 {
789 cout << "Device path: " << file
790 << " does not exist. Spurious udev event? Exiting." << endl;
791 return 0;
792 }
793
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500794 baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
Santosh Puranik85893752020-11-10 21:31:43 +0530795 // Check if we can read the VPD file based on the power state
796 if (js["frus"][file].at(0).value("powerOffOnly", false))
797 {
798 if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
799 getPowerState())
800 {
801 cout << "This VPD cannot be read when power is ON" << endl;
802 return 0;
803 }
804 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500805
SunnySrivastava1984e12b1812020-05-26 02:23:11 -0500806 Binary vpdVector = getVpdDataInVector(js, file);
Alpana Kumari65b83602020-09-01 00:24:56 -0500807 ParserInterface* parser = ParserFactory::getParser(move(vpdVector));
SunnySrivastava1984e12b1812020-05-26 02:23:11 -0500808
809 variant<KeywordVpdMap, Store> parseResult;
810 parseResult = parser->parse();
811
Alpana Kumari2f793042020-08-18 05:51:03 -0500812 try
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530813 {
Alpana Kumari2f793042020-08-18 05:51:03 -0500814 Binary vpdVector = getVpdDataInVector(js, file);
815 ParserInterface* parser = ParserFactory::getParser(move(vpdVector));
SunnySrivastava1984e12b1812020-05-26 02:23:11 -0500816
Alpana Kumari2f793042020-08-18 05:51:03 -0500817 variant<KeywordVpdMap, Store> parseResult;
818 parseResult = parser->parse();
819 if (auto pVal = get_if<Store>(&parseResult))
820 {
821 populateDbus(pVal->getVpdMap(), js, file);
822 }
823 else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
824 {
825 populateDbus(*pVal, js, file);
826 }
827
828 // release the parser object
829 ParserFactory::freeParser(parser);
830 }
831 catch (exception& e)
832 {
833 postFailAction(js, file);
834 throw e;
835 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530836 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500837 catch (const VpdJsonException& ex)
838 {
839 additionalData.emplace("JSON_PATH", ex.getJsonPath());
840 additionalData.emplace("DESCRIPTION", ex.what());
841 createPEL(additionalData, errIntfForJsonFailure);
842
843 cerr << ex.what() << "\n";
844 rc = -1;
845 }
846 catch (const VpdEccException& ex)
847 {
848 additionalData.emplace("DESCRIPTION", "ECC check failed");
849 additionalData.emplace("CALLOUT_INVENTORY_PATH",
850 INVENTORY_PATH + baseFruInventoryPath);
851 createPEL(additionalData, errIntfForEccCheckFail);
852
853 cerr << ex.what() << "\n";
854 rc = -1;
855 }
856 catch (const VpdDataException& ex)
857 {
858 additionalData.emplace("DESCRIPTION", "Invalid VPD data");
859 additionalData.emplace("CALLOUT_INVENTORY_PATH",
860 INVENTORY_PATH + baseFruInventoryPath);
861 createPEL(additionalData, errIntfForInvalidVPD);
862
863 cerr << ex.what() << "\n";
864 rc = -1;
865 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530866 catch (exception& e)
867 {
868 cerr << e.what() << "\n";
869 rc = -1;
870 }
871
872 return rc;
873}