blob: 7f9e383f7add27cf9ef4d2f38cb92fc0bb30ecc6 [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
Santosh Puranik88edeb62020-03-02 12:00:09 +053043/**
Santosh Puranik85893752020-11-10 21:31:43 +053044 * @brief Returns the power state for chassis0
45 */
46static auto getPowerState()
47{
48 // TODO: How do we handle multiple chassis?
49 string powerState{};
50 auto bus = sdbusplus::bus::new_default();
51 auto properties =
52 bus.new_method_call("xyz.openbmc_project.State.Chassis",
53 "/xyz/openbmc_project/state/chassis0",
54 "org.freedesktop.DBus.Properties", "Get");
55 properties.append("xyz.openbmc_project.State.Chassis");
56 properties.append("CurrentPowerState");
57 auto result = bus.call(properties);
58 if (!result.is_method_error())
59 {
60 variant<string> val;
61 result.read(val);
62 if (auto pVal = get_if<string>(&val))
63 {
64 powerState = *pVal;
65 }
66 }
67 cout << "Power state is: " << powerState << endl;
68 return powerState;
69}
70
71/**
Santosh Puranik88edeb62020-03-02 12:00:09 +053072 * @brief Expands location codes
73 */
74static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
75 bool isSystemVpd)
76{
77 auto expanded{unexpanded};
78 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
79 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
80 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
81 size_t idx = expanded.find("fcs");
82 try
83 {
84 if (idx != string::npos)
85 {
86 string fc{};
87 string se{};
88 if (isSystemVpd)
89 {
90 const auto& fcData = vpdMap.at("VCEN").at("FC");
91 const auto& seData = vpdMap.at("VCEN").at("SE");
92 fc = string(fcData.data(), fcData.size());
93 se = string(seData.data(), seData.size());
94 }
95 else
96 {
97 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
98 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
99 }
100
Alpana Kumari81671f62021-02-10 02:21:59 -0600101 // TODO: See if ND0 can be placed in the JSON
102 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND0." + se);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530103 }
104 else
105 {
106 idx = expanded.find("mts");
107 if (idx != string::npos)
108 {
109 string mt{};
110 string se{};
111 if (isSystemVpd)
112 {
113 const auto& mtData = vpdMap.at("VSYS").at("TM");
114 const auto& seData = vpdMap.at("VSYS").at("SE");
115 mt = string(mtData.data(), mtData.size());
116 se = string(seData.data(), seData.size());
117 }
118 else
119 {
120 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
121 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
122 }
123
124 replace(mt.begin(), mt.end(), '-', '.');
125 expanded.replace(idx, 3, mt + "." + se);
126 }
127 }
128 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500129 catch (const exception& e)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530130 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500131 cerr << "Failed to expand location code with exception: " << e.what()
132 << "\n";
Santosh Puranik88edeb62020-03-02 12:00:09 +0530133 }
134 return expanded;
135}
Alpana Kumari2f793042020-08-18 05:51:03 -0500136
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530137/**
138 * @brief Populate FRU specific interfaces.
139 *
140 * This is a common method which handles both
141 * ipz and keyword specific interfaces thus,
142 * reducing the code redundancy.
143 * @param[in] map - Reference to the innermost keyword-value map.
144 * @param[in] preIntrStr - Reference to the interface string.
145 * @param[out] interfaces - Reference to interface map.
146 */
147template <typename T>
148static void populateFruSpecificInterfaces(const T& map,
149 const string& preIntrStr,
150 inventory::InterfaceMap& interfaces)
151{
152 inventory::PropertyMap prop;
153
154 for (const auto& kwVal : map)
155 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530156 auto kw = kwVal.first;
157
158 if (kw[0] == '#')
159 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500160 kw = string("PD_") + kw[1];
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530161 }
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500162 else if (isdigit(kw[0]))
163 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500164 kw = string("N_") + kw;
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500165 }
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000166 if constexpr (is_same<T, KeywordVpdMap>::value)
167 {
168 if (get_if<Binary>(&kwVal.second))
169 {
170 Binary vec(get_if<Binary>(&kwVal.second)->begin(),
171 get_if<Binary>(&kwVal.second)->end());
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000172 prop.emplace(move(kw), move(vec));
173 }
174 else
175 {
176 if (kw == "MemorySizeInKB")
177 {
178 inventory::PropertyMap memProp;
179 auto memVal = get_if<size_t>(&kwVal.second);
180 if (memVal)
181 {
182 memProp.emplace(move(kw),
183 ((*memVal) * CONVERT_MB_TO_KB));
184 interfaces.emplace(
185 "xyz.openbmc_project.Inventory.Item.Dimm",
186 move(memProp));
187 }
188 else
189 {
190 cerr << "MemorySizeInKB value not found in vpd map\n";
191 }
192 }
193 }
194 }
195 else
196 {
197 Binary vec(kwVal.second.begin(), kwVal.second.end());
198 prop.emplace(move(kw), move(vec));
199 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530200 }
201
202 interfaces.emplace(preIntrStr, move(prop));
203}
204
205/**
206 * @brief Populate Interfaces.
207 *
208 * This method populates common and extra interfaces to dbus.
209 * @param[in] js - json object
210 * @param[out] interfaces - Reference to interface map
211 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530212 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530213 */
214template <typename T>
215static void populateInterfaces(const nlohmann::json& js,
216 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530217 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530218{
219 for (const auto& ifs : js.items())
220 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530221 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530222 inventory::PropertyMap props;
223
224 for (const auto& itr : ifs.value().items())
225 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530226 const string& busProp = itr.key();
227
Alpana Kumari31970de2020-02-17 06:49:57 -0600228 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530229 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530230 props.emplace(busProp, itr.value().get<bool>());
231 }
232 else if (itr.value().is_string())
233 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500234 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530235 {
236 if (busProp == "LocationCode" &&
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000237 inf == IBM_LOCATION_CODE_INF)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530238 {
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000239 // TODO deprecate the com.ibm interface later
Santosh Puranik88edeb62020-03-02 12:00:09 +0530240 auto prop = expandLocationCode(
241 itr.value().get<string>(), vpdMap, isSystemVpd);
242 props.emplace(busProp, prop);
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000243 interfaces.emplace(XYZ_LOCATION_CODE_INF, props);
Priyanga Ramasamy69f76022022-01-05 07:10:36 +0000244 interfaces.emplace(IBM_LOCATION_CODE_INF, props);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530245 }
246 else
247 {
248 props.emplace(busProp, itr.value().get<string>());
249 }
250 }
251 else
252 {
253 props.emplace(busProp, itr.value().get<string>());
254 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530255 }
Santosh Puraniked609af2021-06-21 11:30:07 +0530256 else if (itr.value().is_array())
257 {
258 try
259 {
260 props.emplace(busProp, itr.value().get<Binary>());
261 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500262 catch (const nlohmann::detail::type_error& e)
Santosh Puraniked609af2021-06-21 11:30:07 +0530263 {
264 std::cerr << "Type exception: " << e.what() << "\n";
265 // Ignore any type errors
266 }
267 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600268 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530269 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600270 const string& rec = itr.value().value("recordName", "");
271 const string& kw = itr.value().value("keywordName", "");
272 const string& encoding = itr.value().value("encoding", "");
273
Alpana Kumari58e22142020-05-05 00:22:12 -0500274 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530275 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530276 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
277 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600278 {
279 auto encoded =
280 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530281 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600282 }
283 }
Alpana Kumari58e22142020-05-05 00:22:12 -0500284 else if constexpr (is_same<T, KeywordVpdMap>::value)
Alpana Kumari31970de2020-02-17 06:49:57 -0600285 {
286 if (!kw.empty() && vpdMap.count(kw))
287 {
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000288 auto kwValue = get_if<Binary>(&vpdMap.at(kw));
289 auto uintValue = get_if<size_t>(&vpdMap.at(kw));
290
291 if (kwValue)
292 {
293 auto prop =
294 string((*kwValue).begin(), (*kwValue).end());
295
296 auto encoded = encodeKeyword(prop, encoding);
297
298 props.emplace(busProp, encoded);
299 }
300 else if (uintValue)
301 {
302 props.emplace(busProp, *uintValue);
303 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600304 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530305 }
306 }
Matt Spinlerb1e64bb2021-09-08 09:57:48 -0500307 else if (itr.value().is_number())
308 {
309 // For now assume the value is a size_t. In the future it would
310 // be nice to come up with a way to get the type from the JSON.
311 props.emplace(busProp, itr.value().get<size_t>());
312 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530313 }
314 interfaces.emplace(inf, move(props));
315 }
316}
317
Priyanga Ramasamy37233782021-12-09 03:14:02 -0600318/*API to reset EEPROM pointer to a safe position to avoid VPD corruption.
319 * Currently do reset only for DIMM VPD.*/
320static void resetEEPROMPointer(const nlohmann::json& js, const string& file,
321 ifstream& vpdFile)
322{
323 for (const auto& item : js["frus"][file])
324 {
325 if (item.find("extraInterfaces") != item.end())
326 {
327 if (item["extraInterfaces"].find(
328 "xyz.openbmc_project.Inventory.Item.Dimm") !=
329 item["extraInterfaces"].end())
330 {
331 // moves the EEPROM pointer to 2048 'th byte.
332 vpdFile.seekg(2047, std::ios::beg);
333 // Read that byte and discard - to affirm the move
334 // operation.
335 char ch;
336 vpdFile.read(&ch, sizeof(ch));
337 }
338 return;
339 }
340 }
341}
342
Alpana Kumari2f793042020-08-18 05:51:03 -0500343static Binary getVpdDataInVector(const nlohmann::json& js, const string& file)
Alpana Kumari58e22142020-05-05 00:22:12 -0500344{
345 uint32_t offset = 0;
346 // check if offset present?
347 for (const auto& item : js["frus"][file])
348 {
349 if (item.find("offset") != item.end())
350 {
351 offset = item["offset"];
352 }
353 }
354
355 // TODO: Figure out a better way to get max possible VPD size.
Priyanga Ramasamy3c2a2b92021-12-22 00:09:38 -0600356 auto maxVPDSize = std::min(std::filesystem::file_size(file),
357 static_cast<uintmax_t>(65504));
358
Alpana Kumari58e22142020-05-05 00:22:12 -0500359 Binary vpdVector;
Priyanga Ramasamy3c2a2b92021-12-22 00:09:38 -0600360 vpdVector.resize(maxVPDSize);
Alpana Kumari58e22142020-05-05 00:22:12 -0500361 ifstream vpdFile;
362 vpdFile.open(file, ios::binary);
363
364 vpdFile.seekg(offset, ios_base::cur);
Priyanga Ramasamy3c2a2b92021-12-22 00:09:38 -0600365 vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), maxVPDSize);
Alpana Kumari58e22142020-05-05 00:22:12 -0500366 vpdVector.resize(vpdFile.gcount());
367
Priyanga Ramasamy37233782021-12-09 03:14:02 -0600368 resetEEPROMPointer(js, file, vpdFile);
369
Alpana Kumari58e22142020-05-05 00:22:12 -0500370 return vpdVector;
371}
372
Alpana Kumari2f793042020-08-18 05:51:03 -0500373/** This API will be called at the end of VPD collection to perform any post
374 * actions.
375 *
376 * @param[in] json - json object
377 * @param[in] file - eeprom file path
378 */
379static void postFailAction(const nlohmann::json& json, const string& file)
380{
381 if ((json["frus"][file].at(0)).find("postActionFail") ==
382 json["frus"][file].at(0).end())
383 {
384 return;
385 }
386
387 uint8_t pinValue = 0;
388 string pinName;
389
390 for (const auto& postAction :
391 (json["frus"][file].at(0))["postActionFail"].items())
392 {
393 if (postAction.key() == "pin")
394 {
395 pinName = postAction.value();
396 }
397 else if (postAction.key() == "value")
398 {
399 // Get the value to set
400 pinValue = postAction.value();
401 }
402 }
403
404 cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
405
406 try
407 {
408 gpiod::line outputLine = gpiod::find_line(pinName);
409
410 if (!outputLine)
411 {
412 cout << "Couldn't find output line:" << pinName
413 << " on GPIO. Skipping...\n";
414
415 return;
416 }
417 outputLine.request(
418 {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
419 pinValue);
420 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500421 catch (const system_error&)
Alpana Kumari2f793042020-08-18 05:51:03 -0500422 {
423 cerr << "Failed to set post-action GPIO" << endl;
424 }
425}
426
427/** Performs any pre-action needed to get the FRU setup for collection.
428 *
429 * @param[in] json - json object
430 * @param[in] file - eeprom file path
431 */
432static void preAction(const nlohmann::json& json, const string& file)
433{
434 if ((json["frus"][file].at(0)).find("preAction") ==
435 json["frus"][file].at(0).end())
436 {
437 return;
438 }
439
440 uint8_t pinValue = 0;
441 string pinName;
442
443 for (const auto& postAction :
444 (json["frus"][file].at(0))["preAction"].items())
445 {
446 if (postAction.key() == "pin")
447 {
448 pinName = postAction.value();
449 }
450 else if (postAction.key() == "value")
451 {
452 // Get the value to set
453 pinValue = postAction.value();
454 }
455 }
456
457 cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
458 try
459 {
460 gpiod::line outputLine = gpiod::find_line(pinName);
461
462 if (!outputLine)
463 {
464 cout << "Couldn't find output line:" << pinName
465 << " on GPIO. Skipping...\n";
466
467 return;
468 }
469 outputLine.request(
470 {"FRU pre-action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
471 pinValue);
472 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500473 catch (const system_error&)
Alpana Kumari2f793042020-08-18 05:51:03 -0500474 {
475 cerr << "Failed to set pre-action GPIO" << endl;
476 return;
477 }
478
479 // Now bind the device
Alpana Kumari4c3bf5b2021-09-16 07:24:58 -0500480 string bind = json["frus"][file].at(0).value("devAddress", "");
Alpana Kumari2f793042020-08-18 05:51:03 -0500481 cout << "Binding device " << bind << endl;
482 string bindCmd = string("echo \"") + bind +
483 string("\" > /sys/bus/i2c/drivers/at24/bind");
484 cout << bindCmd << endl;
485 executeCmd(bindCmd);
486
487 // Check if device showed up (test for file)
488 if (!fs::exists(file))
489 {
490 cout << "EEPROM " << file << " does not exist. Take failure action"
491 << endl;
492 // If not, then take failure postAction
493 postFailAction(json, file);
494 }
495}
496
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530497/**
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530498 * @brief Set certain one time properties in the inventory
499 * Use this function to insert the Functional and Enabled properties into the
500 * inventory map. This function first checks if the object in question already
501 * has these properties hosted on D-Bus, if the property is already there, it is
502 * not modified, hence the name "one time". If the property is not already
503 * present, it will be added to the map with a suitable default value (true for
504 * Functional and false for Enabled)
505 *
506 * @param[in] object - The inventory D-Bus obejct without the inventory prefix.
507 * @param[inout] interfaces - Reference to a map of inventory interfaces to
508 * which the properties will be attached.
509 */
510static void setOneTimeProperties(const std::string& object,
511 inventory::InterfaceMap& interfaces)
512{
513 auto bus = sdbusplus::bus::new_default();
514 auto objectPath = INVENTORY_PATH + object;
515 auto prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
516 objectPath.c_str(),
517 "org.freedesktop.DBus.Properties", "Get");
518 prop.append("xyz.openbmc_project.State.Decorator.OperationalStatus");
519 prop.append("Functional");
520 try
521 {
522 auto result = bus.call(prop);
523 }
524 catch (const sdbusplus::exception::SdBusError& e)
525 {
526 // Treat as property unavailable
527 inventory::PropertyMap prop;
528 prop.emplace("Functional", true);
529 interfaces.emplace(
530 "xyz.openbmc_project.State.Decorator.OperationalStatus",
531 move(prop));
532 }
533 prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
534 objectPath.c_str(),
535 "org.freedesktop.DBus.Properties", "Get");
536 prop.append("xyz.openbmc_project.Object.Enable");
537 prop.append("Enabled");
538 try
539 {
540 auto result = bus.call(prop);
541 }
542 catch (const sdbusplus::exception::SdBusError& e)
543 {
544 // Treat as property unavailable
545 inventory::PropertyMap prop;
546 prop.emplace("Enabled", false);
547 interfaces.emplace("xyz.openbmc_project.Object.Enable", move(prop));
548 }
549}
550
551/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530552 * @brief Prime the Inventory
553 * Prime the inventory by populating only the location code,
554 * type interface and the inventory object for the frus
555 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530556 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530557 * @param[in] jsObject - Reference to vpd inventory json object
558 * @param[in] vpdMap - Reference to the parsed vpd map
559 *
560 * @returns Map of items in extraInterface.
561 */
562template <typename T>
563inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
564 const T& vpdMap)
565{
566 inventory::ObjectMap objects;
567
568 for (auto& itemFRUS : jsObject["frus"].items())
569 {
Alpana Kumari2f793042020-08-18 05:51:03 -0500570 // Take pre actions
571 preAction(jsObject, itemFRUS.key());
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530572 for (auto& itemEEPROM : itemFRUS.value())
573 {
574 inventory::InterfaceMap interfaces;
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530575 inventory::Object object(itemEEPROM.at("inventoryPath"));
576
Santosh Puranik50f60bf2021-05-26 17:55:06 +0530577 if ((itemFRUS.key() != systemVpdFilePath) &&
578 !itemEEPROM.value("noprime", false))
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530579 {
Alpana Kumaricfd7a752021-02-07 23:23:01 -0600580 inventory::PropertyMap presProp;
581 presProp.emplace("Present", false);
582 interfaces.emplace("xyz.openbmc_project.Inventory.Item",
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530583 presProp);
584 setOneTimeProperties(object, interfaces);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530585 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
586 {
587 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
588 {
589 inventory::PropertyMap props;
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000590 if (eI.key() == IBM_LOCATION_CODE_INF)
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530591 {
592 if constexpr (std::is_same<T, Parsed>::value)
593 {
594 for (auto& lC : eI.value().items())
595 {
596 auto propVal = expandLocationCode(
597 lC.value().get<string>(), vpdMap, true);
598
599 props.emplace(move(lC.key()),
600 move(propVal));
Santosh Puranikb0f37492021-06-21 09:42:47 +0530601 interfaces.emplace(XYZ_LOCATION_CODE_INF,
602 props);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530603 interfaces.emplace(move(eI.key()),
604 move(props));
605 }
606 }
607 }
608 else if (eI.key().find("Inventory.Item.") !=
609 string::npos)
610 {
611 interfaces.emplace(move(eI.key()), move(props));
612 }
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530613 else if (eI.key() ==
614 "xyz.openbmc_project.Inventory.Item")
615 {
616 for (auto& val : eI.value().items())
617 {
618 if (val.key() == "PrettyName")
619 {
620 presProp.emplace(val.key(),
621 val.value().get<string>());
622 }
623 }
624 // Use insert_or_assign here as we may already have
625 // inserted the present property only earlier in
626 // this function under this same interface.
627 interfaces.insert_or_assign(eI.key(),
628 move(presProp));
629 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530630 }
631 }
632 objects.emplace(move(object), move(interfaces));
633 }
634 }
635 }
636 return objects;
637}
638
Alpana Kumari65b83602020-09-01 00:24:56 -0500639/**
640 * @brief This API executes command to set environment variable
641 * And then reboot the system
642 * @param[in] key -env key to set new value
643 * @param[in] value -value to set.
644 */
645void setEnvAndReboot(const string& key, const string& value)
646{
647 // set env and reboot and break.
648 executeCmd("/sbin/fw_setenv", key, value);
Andrew Geissler280197e2020-12-08 20:51:49 -0600649 log<level::INFO>("Rebooting BMC to pick up new device tree");
Alpana Kumari65b83602020-09-01 00:24:56 -0500650 // make dbus call to reboot
651 auto bus = sdbusplus::bus::new_default_system();
652 auto method = bus.new_method_call(
653 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
654 "org.freedesktop.systemd1.Manager", "Reboot");
655 bus.call_noreply(method);
656}
657
658/*
659 * @brief This API checks for env var fitconfig.
660 * If not initialised OR updated as per the current system type,
661 * update this env var and reboot the system.
662 *
663 * @param[in] systemType IM kwd in vpd tells about which system type it is.
664 * */
665void setDevTreeEnv(const string& systemType)
666{
Alpana Kumari37e72702021-11-18 11:18:04 -0600667 // Init with default dtb
668 string newDeviceTree = "conf-aspeed-bmc-ibm-rainier-p1.dtb";
Santosh Puranike5f177a2022-01-24 20:14:46 +0530669 static const deviceTreeMap deviceTreeSystemTypeMap = {
670 {RAINIER_2U, "conf-aspeed-bmc-ibm-rainier-p1.dtb"},
671 {RAINIER_2U_V2, "conf-aspeed-bmc-ibm-rainier.dtb"},
672 {RAINIER_4U, "conf-aspeed-bmc-ibm-rainier-4u-p1.dtb"},
673 {RAINIER_4U_V2, "conf-aspeed-bmc-ibm-rainier-4u.dtb"},
674 {RAINIER_1S4U, "conf-aspeed-bmc-ibm-rainier-1s4u.dtb"},
675 {EVEREST, "conf-aspeed-bmc-ibm-everest.dtb"}};
Alpana Kumari65b83602020-09-01 00:24:56 -0500676
677 if (deviceTreeSystemTypeMap.find(systemType) !=
678 deviceTreeSystemTypeMap.end())
679 {
680 newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
681 }
Alpana Kumari37e72702021-11-18 11:18:04 -0600682 else
683 {
684 // System type not supported
685 cerr << "This System type not found/supported in dtb table "
686 << systemType << ". so system will be booted with default dtb.\n";
687 }
Alpana Kumari65b83602020-09-01 00:24:56 -0500688
689 string readVarValue;
690 bool envVarFound = false;
691
692 vector<string> output = executeCmd("/sbin/fw_printenv");
693 for (const auto& entry : output)
694 {
695 size_t pos = entry.find("=");
696 string key = entry.substr(0, pos);
697 if (key != "fitconfig")
698 {
699 continue;
700 }
701
702 envVarFound = true;
703 if (pos + 1 < entry.size())
704 {
705 readVarValue = entry.substr(pos + 1);
706 if (readVarValue.find(newDeviceTree) != string::npos)
707 {
708 // fitconfig is Updated. No action needed
709 break;
710 }
711 }
712 // set env and reboot and break.
713 setEnvAndReboot(key, newDeviceTree);
714 exit(0);
715 }
716
717 // check If env var Not found
718 if (!envVarFound)
719 {
720 setEnvAndReboot("fitconfig", newDeviceTree);
721 }
722}
723
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530724/**
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500725 * @brief API to call VPD manager to write VPD to EEPROM.
726 * @param[in] Object path.
727 * @param[in] record to be updated.
728 * @param[in] keyword to be updated.
729 * @param[in] keyword data to be updated
730 */
731void updateHardware(const string& objectName, const string& recName,
732 const string& kwdName, const Binary& data)
733{
734 try
735 {
736 auto bus = sdbusplus::bus::new_default();
737 auto properties =
738 bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
739 properties.append(
740 static_cast<sdbusplus::message::object_path>(objectName));
741 properties.append(recName);
742 properties.append(kwdName);
743 properties.append(data);
744 bus.call(properties);
745 }
Patrick Williams8be43342021-09-02 09:33:36 -0500746 catch (const sdbusplus::exception::exception& e)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500747 {
748 std::string what =
749 "VPDManager WriteKeyword api failed for inventory path " +
750 objectName;
751 what += " record " + recName;
752 what += " keyword " + kwdName;
753 what += " with bus error = " + std::string(e.what());
754
755 // map to hold additional data in case of logging pel
756 PelAdditionalData additionalData{};
757 additionalData.emplace("CALLOUT_INVENTORY_PATH", objectName);
758 additionalData.emplace("DESCRIPTION", what);
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500759 createPEL(additionalData, PelSeverity::WARNING, errIntfForBusFailure);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500760 }
761}
762
763/**
764 * @brief API to check if we need to restore system VPD
765 * This functionality is only applicable for IPZ VPD data.
766 * @param[in] vpdMap - IPZ vpd map
767 * @param[in] objectPath - Object path for the FRU
768 * @return EEPROMs with records and keywords updated at standby
769 */
770std::vector<RestoredEeproms> restoreSystemVPD(Parsed& vpdMap,
771 const string& objectPath)
772{
773 // the list of keywords for VSYS record is as per the S0 system. Should be
774 // updated for another type of systems
775 static std::unordered_map<std::string, std::vector<std::string>> svpdKwdMap{
776 {"VSYS", {"BR", "TM", "SE", "SU", "RB"}},
777 {"VCEN", {"FC", "SE"}},
778 {"LXR0", {"LX"}}};
779
780 // vector to hold all the EEPROMs updated at standby
781 std::vector<RestoredEeproms> updatedEeproms = {};
782
783 for (const auto& systemRecKwdPair : svpdKwdMap)
784 {
785 auto it = vpdMap.find(systemRecKwdPair.first);
786
787 // check if record is found in map we got by parser
788 if (it != vpdMap.end())
789 {
790 const auto& kwdListForRecord = systemRecKwdPair.second;
791 for (const auto& keyword : kwdListForRecord)
792 {
793 DbusPropertyMap& kwdValMap = it->second;
794 auto iterator = kwdValMap.find(keyword);
795
796 if (iterator != kwdValMap.end())
797 {
798 string& kwdValue = iterator->second;
799
800 // check bus data
801 const string& recordName = systemRecKwdPair.first;
802 const string& busValue = readBusProperty(
803 objectPath, ipzVpdInf + recordName, keyword);
804
805 if (busValue.find_first_not_of(' ') != string::npos)
806 {
807 if (kwdValue.find_first_not_of(' ') != string::npos)
808 {
809 // both the data are present, check for mismatch
810 if (busValue != kwdValue)
811 {
812 string errMsg = "VPD data mismatch on cache "
813 "and hardware for record: ";
814 errMsg += (*it).first;
815 errMsg += " and keyword: ";
816 errMsg += keyword;
817
818 // data mismatch
819 PelAdditionalData additionalData;
820 additionalData.emplace("CALLOUT_INVENTORY_PATH",
821 objectPath);
822
823 additionalData.emplace("DESCRIPTION", errMsg);
824
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500825 createPEL(additionalData, PelSeverity::WARNING,
826 errIntfForInvalidVPD);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500827 }
828 }
829 else
830 {
831 // implies hardware data is blank
832 // update the map
833 Binary busData(busValue.begin(), busValue.end());
834
835 updatedEeproms.push_back(std::make_tuple(
836 objectPath, recordName, keyword, busData));
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500837
Sunny Srivastava90a63b92021-05-26 06:30:24 -0500838 // update the map as well, so that cache data is not
839 // updated as blank while populating VPD map on Dbus
840 // in populateDBus Api
841 kwdValue = busValue;
842 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500843 }
844 else if (kwdValue.find_first_not_of(' ') == string::npos)
845 {
846 string errMsg = "VPD is blank on both cache and "
847 "hardware for record: ";
848 errMsg += (*it).first;
849 errMsg += " and keyword: ";
850 errMsg += keyword;
851 errMsg += ". SSR need to update hardware VPD.";
852
853 // both the data are blanks, log PEL
854 PelAdditionalData additionalData;
855 additionalData.emplace("CALLOUT_INVENTORY_PATH",
856 objectPath);
857
858 additionalData.emplace("DESCRIPTION", errMsg);
859
860 // log PEL TODO: Block IPL
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500861 createPEL(additionalData, PelSeverity::ERROR,
862 errIntfForBlankSystemVPD);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500863 continue;
864 }
865 }
866 }
867 }
868 }
869
870 return updatedEeproms;
871}
872
873/**
alpana077ce68722021-07-25 13:23:59 -0500874 * @brief This checks for is this FRU a processor
875 * And if yes, then checks for is this primary
876 *
877 * @param[in] js- vpd json to get the information about this FRU
878 * @param[in] filePath- FRU vpd
879 *
880 * @return true/false
881 */
882bool isThisPrimaryProcessor(nlohmann::json& js, const string& filePath)
883{
884 bool isProcessor = false;
885 bool isPrimary = false;
886
887 for (const auto& item : js["frus"][filePath])
888 {
889 if (item.find("extraInterfaces") != item.end())
890 {
891 for (const auto& eI : item["extraInterfaces"].items())
892 {
893 if (eI.key().find("Inventory.Item.Cpu") != string::npos)
894 {
895 isProcessor = true;
896 }
897 }
898 }
899
900 if (isProcessor)
901 {
902 string cpuType = item.value("cpuType", "");
903 if (cpuType == "primary")
904 {
905 isPrimary = true;
906 }
907 }
908 }
909
910 return (isProcessor && isPrimary);
911}
912
913/**
914 * @brief This finds DIMM vpd in vpd json and enables them by binding the device
915 * driver
916 * @param[in] js- vpd json to iterate through and take action if it is DIMM
917 */
918void doEnableAllDimms(nlohmann::json& js)
919{
920 // iterate over each fru
921 for (const auto& eachFru : js["frus"].items())
922 {
923 // skip the driver binding if eeprom already exists
924 if (fs::exists(eachFru.key()))
925 {
926 continue;
927 }
928
929 for (const auto& eachInventory : eachFru.value())
930 {
931 if (eachInventory.find("extraInterfaces") != eachInventory.end())
932 {
933 for (const auto& eI : eachInventory["extraInterfaces"].items())
934 {
935 if (eI.key().find("Inventory.Item.Dimm") != string::npos)
936 {
937 string dimmVpd = eachFru.key();
938 // fetch it from
939 // "/sys/bus/i2c/drivers/at24/414-0050/eeprom"
940
941 regex matchPatern("([0-9]+-[0-9]{4})");
942 smatch matchFound;
943 if (regex_search(dimmVpd, matchFound, matchPatern))
944 {
945 vector<string> i2cReg;
946 boost::split(i2cReg, matchFound.str(0),
947 boost::is_any_of("-"));
948
949 // remove 0s from begining
950 const regex pattern("^0+(?!$)");
951 for (auto& i : i2cReg)
952 {
953 i = regex_replace(i, pattern, "");
954 }
955
956 if (i2cReg.size() == 2)
957 {
958 // echo 24c32 0x50 >
959 // /sys/bus/i2c/devices/i2c-16/new_device
960 string cmnd = "echo 24c32 0x" + i2cReg[1] +
961 " > /sys/bus/i2c/devices/i2c-" +
962 i2cReg[0] + "/new_device";
963
964 executeCmd(cmnd);
965 }
966 }
967 }
968 }
969 }
970 }
971 }
972}
973
974/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530975 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530976 * This method invokes all the populateInterface functions
977 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530978 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
979 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530980 * @param[in] js - Inventory json object
981 * @param[in] filePath - Path of the vpd file
982 * @param[in] preIntrStr - Interface string
983 */
984template <typename T>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500985static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530986{
987 inventory::InterfaceMap interfaces;
988 inventory::ObjectMap objects;
989 inventory::PropertyMap prop;
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -0600990 string ccinFromVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530991
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500992 // map to hold all the keywords whose value has been changed at standby
993 vector<RestoredEeproms> updatedEeproms = {};
994
Santosh Puranik50f60bf2021-05-26 17:55:06 +0530995 bool isSystemVpd = (filePath == systemVpdFilePath);
996 if constexpr (is_same<T, Parsed>::value)
997 {
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -0600998 ccinFromVpd = getKwVal(vpdMap, "VINI", "CC");
999 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1000 ::toupper);
1001
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301002 if (isSystemVpd)
1003 {
1004 std::vector<std::string> interfaces = {motherBoardInterface};
1005 // call mapper to check for object path creation
1006 MapperResponse subTree =
1007 getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
1008 string mboardPath =
1009 js["frus"][filePath].at(0).value("inventoryPath", "");
1010
1011 // Attempt system VPD restore if we have a motherboard
1012 // object in the inventory.
1013 if ((subTree.size() != 0) &&
1014 (subTree.find(pimPath + mboardPath) != subTree.end()))
1015 {
1016 updatedEeproms = restoreSystemVPD(vpdMap, mboardPath);
1017 }
1018 else
1019 {
1020 log<level::ERR>("No object path found");
1021 }
1022 }
alpana077ce68722021-07-25 13:23:59 -05001023 else
1024 {
1025 // check if it is processor vpd.
1026 auto isPrimaryCpu = isThisPrimaryProcessor(js, filePath);
1027
1028 if (isPrimaryCpu)
1029 {
1030 auto ddVersion = getKwVal(vpdMap, "CRP0", "DD");
1031
1032 auto chipVersion = atoi(ddVersion.substr(1, 2).c_str());
1033
1034 if (chipVersion >= 2)
1035 {
1036 doEnableAllDimms(js);
1037 }
1038 }
1039 }
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301040 }
1041
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001042 if (isSystemVpd)
1043 {
1044 string systemJsonName{};
1045 if constexpr (is_same<T, Parsed>::value)
1046 {
1047 // pick the right system json
1048 systemJsonName = getSystemsJson(vpdMap);
1049 }
1050
1051 fs::path target = systemJsonName;
1052 fs::path link = INVENTORY_JSON_SYM_LINK;
1053
1054 // Create the directory for hosting the symlink
1055 fs::create_directories(VPD_FILES_PATH);
1056 // unlink the symlink previously created (if any)
1057 remove(INVENTORY_JSON_SYM_LINK);
1058 // create a new symlink based on the system
1059 fs::create_symlink(target, link);
1060
1061 // Reloading the json
1062 ifstream inventoryJson(link);
1063 js = json::parse(inventoryJson);
1064 inventoryJson.close();
1065 }
1066
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301067 for (const auto& item : js["frus"][filePath])
1068 {
1069 const auto& objectPath = item["inventoryPath"];
1070 sdbusplus::message::object_path object(objectPath);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001071
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001072 vector<string> ccinList;
1073 if (item.find("ccin") != item.end())
1074 {
1075 for (const auto& cc : item["ccin"])
1076 {
1077 string ccin = cc;
1078 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1079 ccinList.push_back(ccin);
1080 }
1081 }
1082
1083 if (!ccinFromVpd.empty() && !ccinList.empty() &&
1084 (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1085 ccinList.end()))
1086 {
1087 continue;
1088 }
1089
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301090 if (isSystemVpd)
1091 {
1092 // Populate one time properties for the system VPD and its sub-frus.
1093 // For the remaining FRUs, this will get handled as a part of
1094 // priming the inventory.
1095 setOneTimeProperties(objectPath, interfaces);
1096 }
1097
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301098 // Populate the VPD keywords and the common interfaces only if we
1099 // are asked to inherit that data from the VPD, else only add the
1100 // extraInterfaces.
1101 if (item.value("inherit", true))
1102 {
Alpana Kumari58e22142020-05-05 00:22:12 -05001103 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301104 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301105 // Each record in the VPD becomes an interface and all
1106 // keyword within the record are properties under that
1107 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301108 for (const auto& record : vpdMap)
1109 {
1110 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001111 record.second, ipzVpdInf + record.first, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301112 }
1113 }
Alpana Kumari58e22142020-05-05 00:22:12 -05001114 else if constexpr (is_same<T, KeywordVpdMap>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301115 {
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001116 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301117 }
Santosh Puranik88edeb62020-03-02 12:00:09 +05301118 if (js.find("commonInterfaces") != js.end())
1119 {
1120 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
1121 isSystemVpd);
1122 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301123 }
Santosh Puranik0859eb62020-03-16 02:56:29 -05001124 else
1125 {
1126 // Check if we have been asked to inherit specific record(s)
Alpana Kumari58e22142020-05-05 00:22:12 -05001127 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik0859eb62020-03-16 02:56:29 -05001128 {
1129 if (item.find("copyRecords") != item.end())
1130 {
1131 for (const auto& record : item["copyRecords"])
1132 {
1133 const string& recordName = record;
1134 if (vpdMap.find(recordName) != vpdMap.end())
1135 {
1136 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001137 vpdMap.at(recordName), ipzVpdInf + recordName,
Santosh Puranik0859eb62020-03-16 02:56:29 -05001138 interfaces);
1139 }
1140 }
1141 }
1142 }
1143 }
Alpana Kumari58e22142020-05-05 00:22:12 -05001144 if (item.value("inheritEI", true))
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301145 {
Alpana Kumari58e22142020-05-05 00:22:12 -05001146 // Populate interfaces and properties that are common to every FRU
1147 // and additional interface that might be defined on a per-FRU
1148 // basis.
1149 if (item.find("extraInterfaces") != item.end())
1150 {
1151 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
1152 isSystemVpd);
1153 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301154 }
1155 objects.emplace(move(object), move(interfaces));
1156 }
1157
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301158 if (isSystemVpd)
1159 {
1160 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
1161 objects.insert(primeObject.begin(), primeObject.end());
Alpana Kumari65b83602020-09-01 00:24:56 -05001162
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001163 // if system VPD has been restored at standby, update the EEPROM
1164 for (const auto& item : updatedEeproms)
1165 {
1166 updateHardware(get<0>(item), get<1>(item), get<2>(item),
1167 get<3>(item));
1168 }
Alpana Kumarif05effd2021-04-07 07:32:53 -05001169
1170 // set the U-boot environment variable for device-tree
1171 if constexpr (is_same<T, Parsed>::value)
1172 {
Santosh Puranike5f177a2022-01-24 20:14:46 +05301173 setDevTreeEnv(fs::path(getSystemsJson(vpdMap)).filename());
Alpana Kumarif05effd2021-04-07 07:32:53 -05001174 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301175 }
1176
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301177 // Notify PIM
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05001178 common::utility::callPIM(move(objects));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301179}
1180
1181int main(int argc, char** argv)
1182{
1183 int rc = 0;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001184 json js{};
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001185 Binary vpdVector{};
1186 string file{};
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001187 // map to hold additional data in case of logging pel
1188 PelAdditionalData additionalData{};
1189
1190 // this is needed to hold base fru inventory path in case there is ECC or
1191 // vpd exception while parsing the file
1192 std::string baseFruInventoryPath = {};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301193
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001194 // severity for PEL
1195 PelSeverity pelSeverity = PelSeverity::WARNING;
1196
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301197 try
1198 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301199 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
1200 "in DBUS"};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301201
1202 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
Alpana Kumari2f793042020-08-18 05:51:03 -05001203 ->required();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301204
1205 CLI11_PARSE(app, argc, argv);
1206
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001207 // PEL severity should be ERROR in case of any system VPD failure
1208 if (file == systemVpdFilePath)
1209 {
1210 pelSeverity = PelSeverity::ERROR;
1211 }
1212
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301213 auto jsonToParse = INVENTORY_JSON_DEFAULT;
1214
1215 // If the symlink exists, it means it has been setup for us, switch the
1216 // path
1217 if (fs::exists(INVENTORY_JSON_SYM_LINK))
1218 {
1219 jsonToParse = INVENTORY_JSON_SYM_LINK;
1220 }
1221
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301222 // Make sure that the file path we get is for a supported EEPROM
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301223 ifstream inventoryJson(jsonToParse);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001224 if (!inventoryJson)
1225 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001226 throw(VpdJsonException("Failed to access Json path", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001227 }
1228
1229 try
1230 {
1231 js = json::parse(inventoryJson);
1232 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001233 catch (const json::parse_error& ex)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001234 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001235 throw(VpdJsonException("Json parsing failed", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001236 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301237
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301238 // Do we have the mandatory "frus" section?
1239 if (js.find("frus") == js.end())
1240 {
1241 throw(VpdJsonException("FRUs section not found in JSON",
1242 jsonToParse));
1243 }
1244
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301245 // Check if it's a udev path - patterned as(/ahb/ahb:apb/ahb:apb:bus@)
1246 if (file.find("/ahb:apb") != string::npos)
1247 {
1248 // Translate udev path to a generic /sys/bus/.. file path.
1249 udevToGenericPath(file);
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301250
1251 if ((js["frus"].find(file) != js["frus"].end()) &&
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301252 (file == systemVpdFilePath))
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301253 {
1254 return 0;
1255 }
1256 }
1257
1258 if (file.empty())
1259 {
1260 cerr << "The EEPROM path <" << file << "> is not valid.";
1261 return 0;
1262 }
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301263 if (js["frus"].find(file) == js["frus"].end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301264 {
Santosh Puranik88edeb62020-03-02 12:00:09 +05301265 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301266 }
1267
Alpana Kumari2f793042020-08-18 05:51:03 -05001268 if (!fs::exists(file))
1269 {
1270 cout << "Device path: " << file
1271 << " does not exist. Spurious udev event? Exiting." << endl;
1272 return 0;
1273 }
1274
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001275 baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
Santosh Puranik85893752020-11-10 21:31:43 +05301276 // Check if we can read the VPD file based on the power state
Santosh Puranik27a5e952021-10-07 22:08:01 -05001277 // We skip reading VPD when the power is ON in two scenarios:
1278 // 1) The eeprom we are trying to read is that of the system VPD
1279 // 2) The JSON tells us that the FRU EEPROM cannot be read when
1280 // we are powered ON.
1281 if (js["frus"][file].at(0).value("powerOffOnly", false) ||
1282 (file == systemVpdFilePath))
Santosh Puranik85893752020-11-10 21:31:43 +05301283 {
1284 if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
1285 getPowerState())
1286 {
1287 cout << "This VPD cannot be read when power is ON" << endl;
1288 return 0;
1289 }
1290 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001291
Alpana Kumari2f793042020-08-18 05:51:03 -05001292 try
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301293 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001294 vpdVector = getVpdDataInVector(js, file);
PriyangaRamasamy33c61c22021-04-06 11:15:57 -05001295 ParserInterface* parser = ParserFactory::getParser(vpdVector);
Alpana Kumari2f793042020-08-18 05:51:03 -05001296 variant<KeywordVpdMap, Store> parseResult;
1297 parseResult = parser->parse();
SunnySrivastava19849a195542020-09-07 06:04:50 -05001298
Alpana Kumari2f793042020-08-18 05:51:03 -05001299 if (auto pVal = get_if<Store>(&parseResult))
1300 {
1301 populateDbus(pVal->getVpdMap(), js, file);
1302 }
1303 else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
1304 {
1305 populateDbus(*pVal, js, file);
1306 }
1307
1308 // release the parser object
1309 ParserFactory::freeParser(parser);
1310 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001311 catch (const exception& e)
Alpana Kumari2f793042020-08-18 05:51:03 -05001312 {
1313 postFailAction(js, file);
PriyangaRamasamya504c3e2020-12-06 12:14:52 -06001314 throw;
Alpana Kumari2f793042020-08-18 05:51:03 -05001315 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301316 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001317 catch (const VpdJsonException& ex)
1318 {
1319 additionalData.emplace("JSON_PATH", ex.getJsonPath());
1320 additionalData.emplace("DESCRIPTION", ex.what());
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001321 createPEL(additionalData, pelSeverity, errIntfForJsonFailure);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001322
1323 cerr << ex.what() << "\n";
1324 rc = -1;
1325 }
1326 catch (const VpdEccException& ex)
1327 {
1328 additionalData.emplace("DESCRIPTION", "ECC check failed");
1329 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1330 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001331 createPEL(additionalData, pelSeverity, errIntfForEccCheckFail);
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001332 dumpBadVpd(file, vpdVector);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001333 cerr << ex.what() << "\n";
1334 rc = -1;
1335 }
1336 catch (const VpdDataException& ex)
1337 {
1338 additionalData.emplace("DESCRIPTION", "Invalid VPD data");
1339 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1340 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001341 createPEL(additionalData, pelSeverity, errIntfForInvalidVPD);
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001342 dumpBadVpd(file, vpdVector);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001343 cerr << ex.what() << "\n";
1344 rc = -1;
1345 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001346 catch (const exception& e)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301347 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001348 dumpBadVpd(file, vpdVector);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301349 cerr << e.what() << "\n";
1350 rc = -1;
1351 }
1352
1353 return rc;
Alpana Kumari37e72702021-11-18 11:18:04 -06001354}