blob: 43d828077c6955a4392c9815770520ad81fb76cf [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
Alpana Kumariab1e22c2021-11-24 11:03:38 -0600685 string err = "This System type not found/supported in dtb table " +
686 systemType +
687 ".Please check the HW and IM keywords in the system "
688 "VPD.Breaking...";
689
690 // map to hold additional data in case of logging pel
691 PelAdditionalData additionalData{};
692 additionalData.emplace("DESCRIPTION", err);
693 createPEL(additionalData, PelSeverity::WARNING,
694 errIntfForInvalidSystemType);
695 exit(-1);
Alpana Kumari37e72702021-11-18 11:18:04 -0600696 }
Alpana Kumari65b83602020-09-01 00:24:56 -0500697
698 string readVarValue;
699 bool envVarFound = false;
700
701 vector<string> output = executeCmd("/sbin/fw_printenv");
702 for (const auto& entry : output)
703 {
704 size_t pos = entry.find("=");
705 string key = entry.substr(0, pos);
706 if (key != "fitconfig")
707 {
708 continue;
709 }
710
711 envVarFound = true;
712 if (pos + 1 < entry.size())
713 {
714 readVarValue = entry.substr(pos + 1);
715 if (readVarValue.find(newDeviceTree) != string::npos)
716 {
717 // fitconfig is Updated. No action needed
718 break;
719 }
720 }
721 // set env and reboot and break.
722 setEnvAndReboot(key, newDeviceTree);
723 exit(0);
724 }
725
726 // check If env var Not found
727 if (!envVarFound)
728 {
729 setEnvAndReboot("fitconfig", newDeviceTree);
730 }
731}
732
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530733/**
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500734 * @brief API to call VPD manager to write VPD to EEPROM.
735 * @param[in] Object path.
736 * @param[in] record to be updated.
737 * @param[in] keyword to be updated.
738 * @param[in] keyword data to be updated
739 */
740void updateHardware(const string& objectName, const string& recName,
741 const string& kwdName, const Binary& data)
742{
743 try
744 {
745 auto bus = sdbusplus::bus::new_default();
746 auto properties =
747 bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
748 properties.append(
749 static_cast<sdbusplus::message::object_path>(objectName));
750 properties.append(recName);
751 properties.append(kwdName);
752 properties.append(data);
753 bus.call(properties);
754 }
Patrick Williams8be43342021-09-02 09:33:36 -0500755 catch (const sdbusplus::exception::exception& e)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500756 {
757 std::string what =
758 "VPDManager WriteKeyword api failed for inventory path " +
759 objectName;
760 what += " record " + recName;
761 what += " keyword " + kwdName;
762 what += " with bus error = " + std::string(e.what());
763
764 // map to hold additional data in case of logging pel
765 PelAdditionalData additionalData{};
766 additionalData.emplace("CALLOUT_INVENTORY_PATH", objectName);
767 additionalData.emplace("DESCRIPTION", what);
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500768 createPEL(additionalData, PelSeverity::WARNING, errIntfForBusFailure);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500769 }
770}
771
772/**
773 * @brief API to check if we need to restore system VPD
774 * This functionality is only applicable for IPZ VPD data.
775 * @param[in] vpdMap - IPZ vpd map
776 * @param[in] objectPath - Object path for the FRU
777 * @return EEPROMs with records and keywords updated at standby
778 */
779std::vector<RestoredEeproms> restoreSystemVPD(Parsed& vpdMap,
780 const string& objectPath)
781{
782 // the list of keywords for VSYS record is as per the S0 system. Should be
783 // updated for another type of systems
784 static std::unordered_map<std::string, std::vector<std::string>> svpdKwdMap{
785 {"VSYS", {"BR", "TM", "SE", "SU", "RB"}},
786 {"VCEN", {"FC", "SE"}},
787 {"LXR0", {"LX"}}};
788
789 // vector to hold all the EEPROMs updated at standby
790 std::vector<RestoredEeproms> updatedEeproms = {};
791
792 for (const auto& systemRecKwdPair : svpdKwdMap)
793 {
794 auto it = vpdMap.find(systemRecKwdPair.first);
795
796 // check if record is found in map we got by parser
797 if (it != vpdMap.end())
798 {
799 const auto& kwdListForRecord = systemRecKwdPair.second;
800 for (const auto& keyword : kwdListForRecord)
801 {
802 DbusPropertyMap& kwdValMap = it->second;
803 auto iterator = kwdValMap.find(keyword);
804
805 if (iterator != kwdValMap.end())
806 {
807 string& kwdValue = iterator->second;
808
809 // check bus data
810 const string& recordName = systemRecKwdPair.first;
811 const string& busValue = readBusProperty(
812 objectPath, ipzVpdInf + recordName, keyword);
813
814 if (busValue.find_first_not_of(' ') != string::npos)
815 {
816 if (kwdValue.find_first_not_of(' ') != string::npos)
817 {
818 // both the data are present, check for mismatch
819 if (busValue != kwdValue)
820 {
821 string errMsg = "VPD data mismatch on cache "
822 "and hardware for record: ";
823 errMsg += (*it).first;
824 errMsg += " and keyword: ";
825 errMsg += keyword;
826
827 // data mismatch
828 PelAdditionalData additionalData;
829 additionalData.emplace("CALLOUT_INVENTORY_PATH",
830 objectPath);
831
832 additionalData.emplace("DESCRIPTION", errMsg);
833
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500834 createPEL(additionalData, PelSeverity::WARNING,
835 errIntfForInvalidVPD);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500836 }
837 }
838 else
839 {
840 // implies hardware data is blank
841 // update the map
842 Binary busData(busValue.begin(), busValue.end());
843
844 updatedEeproms.push_back(std::make_tuple(
845 objectPath, recordName, keyword, busData));
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500846
Sunny Srivastava90a63b92021-05-26 06:30:24 -0500847 // update the map as well, so that cache data is not
848 // updated as blank while populating VPD map on Dbus
849 // in populateDBus Api
850 kwdValue = busValue;
851 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500852 }
853 else if (kwdValue.find_first_not_of(' ') == string::npos)
854 {
855 string errMsg = "VPD is blank on both cache and "
856 "hardware for record: ";
857 errMsg += (*it).first;
858 errMsg += " and keyword: ";
859 errMsg += keyword;
860 errMsg += ". SSR need to update hardware VPD.";
861
862 // both the data are blanks, log PEL
863 PelAdditionalData additionalData;
864 additionalData.emplace("CALLOUT_INVENTORY_PATH",
865 objectPath);
866
867 additionalData.emplace("DESCRIPTION", errMsg);
868
869 // log PEL TODO: Block IPL
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500870 createPEL(additionalData, PelSeverity::ERROR,
871 errIntfForBlankSystemVPD);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500872 continue;
873 }
874 }
875 }
876 }
877 }
878
879 return updatedEeproms;
880}
881
882/**
alpana077ce68722021-07-25 13:23:59 -0500883 * @brief This checks for is this FRU a processor
884 * And if yes, then checks for is this primary
885 *
886 * @param[in] js- vpd json to get the information about this FRU
887 * @param[in] filePath- FRU vpd
888 *
889 * @return true/false
890 */
891bool isThisPrimaryProcessor(nlohmann::json& js, const string& filePath)
892{
893 bool isProcessor = false;
894 bool isPrimary = false;
895
896 for (const auto& item : js["frus"][filePath])
897 {
898 if (item.find("extraInterfaces") != item.end())
899 {
900 for (const auto& eI : item["extraInterfaces"].items())
901 {
902 if (eI.key().find("Inventory.Item.Cpu") != string::npos)
903 {
904 isProcessor = true;
905 }
906 }
907 }
908
909 if (isProcessor)
910 {
911 string cpuType = item.value("cpuType", "");
912 if (cpuType == "primary")
913 {
914 isPrimary = true;
915 }
916 }
917 }
918
919 return (isProcessor && isPrimary);
920}
921
922/**
923 * @brief This finds DIMM vpd in vpd json and enables them by binding the device
924 * driver
925 * @param[in] js- vpd json to iterate through and take action if it is DIMM
926 */
927void doEnableAllDimms(nlohmann::json& js)
928{
929 // iterate over each fru
930 for (const auto& eachFru : js["frus"].items())
931 {
932 // skip the driver binding if eeprom already exists
933 if (fs::exists(eachFru.key()))
934 {
935 continue;
936 }
937
938 for (const auto& eachInventory : eachFru.value())
939 {
940 if (eachInventory.find("extraInterfaces") != eachInventory.end())
941 {
942 for (const auto& eI : eachInventory["extraInterfaces"].items())
943 {
944 if (eI.key().find("Inventory.Item.Dimm") != string::npos)
945 {
946 string dimmVpd = eachFru.key();
947 // fetch it from
948 // "/sys/bus/i2c/drivers/at24/414-0050/eeprom"
949
950 regex matchPatern("([0-9]+-[0-9]{4})");
951 smatch matchFound;
952 if (regex_search(dimmVpd, matchFound, matchPatern))
953 {
954 vector<string> i2cReg;
955 boost::split(i2cReg, matchFound.str(0),
956 boost::is_any_of("-"));
957
958 // remove 0s from begining
959 const regex pattern("^0+(?!$)");
960 for (auto& i : i2cReg)
961 {
962 i = regex_replace(i, pattern, "");
963 }
964
965 if (i2cReg.size() == 2)
966 {
967 // echo 24c32 0x50 >
968 // /sys/bus/i2c/devices/i2c-16/new_device
969 string cmnd = "echo 24c32 0x" + i2cReg[1] +
970 " > /sys/bus/i2c/devices/i2c-" +
971 i2cReg[0] + "/new_device";
972
973 executeCmd(cmnd);
974 }
975 }
976 }
977 }
978 }
979 }
980 }
981}
982
983/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530984 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530985 * This method invokes all the populateInterface functions
986 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530987 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
988 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530989 * @param[in] js - Inventory json object
990 * @param[in] filePath - Path of the vpd file
991 * @param[in] preIntrStr - Interface string
992 */
993template <typename T>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500994static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530995{
996 inventory::InterfaceMap interfaces;
997 inventory::ObjectMap objects;
998 inventory::PropertyMap prop;
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -0600999 string ccinFromVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301000
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001001 // map to hold all the keywords whose value has been changed at standby
1002 vector<RestoredEeproms> updatedEeproms = {};
1003
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301004 bool isSystemVpd = (filePath == systemVpdFilePath);
1005 if constexpr (is_same<T, Parsed>::value)
1006 {
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001007 ccinFromVpd = getKwVal(vpdMap, "VINI", "CC");
1008 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1009 ::toupper);
1010
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301011 if (isSystemVpd)
1012 {
1013 std::vector<std::string> interfaces = {motherBoardInterface};
1014 // call mapper to check for object path creation
1015 MapperResponse subTree =
1016 getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
1017 string mboardPath =
1018 js["frus"][filePath].at(0).value("inventoryPath", "");
1019
1020 // Attempt system VPD restore if we have a motherboard
1021 // object in the inventory.
1022 if ((subTree.size() != 0) &&
1023 (subTree.find(pimPath + mboardPath) != subTree.end()))
1024 {
1025 updatedEeproms = restoreSystemVPD(vpdMap, mboardPath);
1026 }
1027 else
1028 {
1029 log<level::ERR>("No object path found");
1030 }
1031 }
alpana077ce68722021-07-25 13:23:59 -05001032 else
1033 {
1034 // check if it is processor vpd.
1035 auto isPrimaryCpu = isThisPrimaryProcessor(js, filePath);
1036
1037 if (isPrimaryCpu)
1038 {
1039 auto ddVersion = getKwVal(vpdMap, "CRP0", "DD");
1040
1041 auto chipVersion = atoi(ddVersion.substr(1, 2).c_str());
1042
1043 if (chipVersion >= 2)
1044 {
1045 doEnableAllDimms(js);
1046 }
1047 }
1048 }
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301049 }
1050
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001051 if (isSystemVpd)
1052 {
1053 string systemJsonName{};
1054 if constexpr (is_same<T, Parsed>::value)
1055 {
1056 // pick the right system json
1057 systemJsonName = getSystemsJson(vpdMap);
1058 }
1059
1060 fs::path target = systemJsonName;
1061 fs::path link = INVENTORY_JSON_SYM_LINK;
1062
1063 // Create the directory for hosting the symlink
1064 fs::create_directories(VPD_FILES_PATH);
1065 // unlink the symlink previously created (if any)
1066 remove(INVENTORY_JSON_SYM_LINK);
1067 // create a new symlink based on the system
1068 fs::create_symlink(target, link);
1069
1070 // Reloading the json
1071 ifstream inventoryJson(link);
1072 js = json::parse(inventoryJson);
1073 inventoryJson.close();
1074 }
1075
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301076 for (const auto& item : js["frus"][filePath])
1077 {
1078 const auto& objectPath = item["inventoryPath"];
1079 sdbusplus::message::object_path object(objectPath);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001080
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001081 vector<string> ccinList;
1082 if (item.find("ccin") != item.end())
1083 {
1084 for (const auto& cc : item["ccin"])
1085 {
1086 string ccin = cc;
1087 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1088 ccinList.push_back(ccin);
1089 }
1090 }
1091
1092 if (!ccinFromVpd.empty() && !ccinList.empty() &&
1093 (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1094 ccinList.end()))
1095 {
1096 continue;
1097 }
1098
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301099 if (isSystemVpd)
1100 {
1101 // Populate one time properties for the system VPD and its sub-frus.
1102 // For the remaining FRUs, this will get handled as a part of
1103 // priming the inventory.
1104 setOneTimeProperties(objectPath, interfaces);
1105 }
1106
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301107 // Populate the VPD keywords and the common interfaces only if we
1108 // are asked to inherit that data from the VPD, else only add the
1109 // extraInterfaces.
1110 if (item.value("inherit", true))
1111 {
Alpana Kumari58e22142020-05-05 00:22:12 -05001112 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301113 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301114 // Each record in the VPD becomes an interface and all
1115 // keyword within the record are properties under that
1116 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301117 for (const auto& record : vpdMap)
1118 {
1119 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001120 record.second, ipzVpdInf + record.first, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301121 }
1122 }
Alpana Kumari58e22142020-05-05 00:22:12 -05001123 else if constexpr (is_same<T, KeywordVpdMap>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301124 {
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001125 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301126 }
Santosh Puranik88edeb62020-03-02 12:00:09 +05301127 if (js.find("commonInterfaces") != js.end())
1128 {
1129 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
1130 isSystemVpd);
1131 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301132 }
Santosh Puranik0859eb62020-03-16 02:56:29 -05001133 else
1134 {
1135 // Check if we have been asked to inherit specific record(s)
Alpana Kumari58e22142020-05-05 00:22:12 -05001136 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik0859eb62020-03-16 02:56:29 -05001137 {
1138 if (item.find("copyRecords") != item.end())
1139 {
1140 for (const auto& record : item["copyRecords"])
1141 {
1142 const string& recordName = record;
1143 if (vpdMap.find(recordName) != vpdMap.end())
1144 {
1145 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001146 vpdMap.at(recordName), ipzVpdInf + recordName,
Santosh Puranik0859eb62020-03-16 02:56:29 -05001147 interfaces);
1148 }
1149 }
1150 }
1151 }
1152 }
Alpana Kumari58e22142020-05-05 00:22:12 -05001153 if (item.value("inheritEI", true))
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301154 {
Alpana Kumari58e22142020-05-05 00:22:12 -05001155 // Populate interfaces and properties that are common to every FRU
1156 // and additional interface that might be defined on a per-FRU
1157 // basis.
1158 if (item.find("extraInterfaces") != item.end())
1159 {
1160 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
1161 isSystemVpd);
1162 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301163 }
1164 objects.emplace(move(object), move(interfaces));
1165 }
1166
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301167 if (isSystemVpd)
1168 {
1169 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
1170 objects.insert(primeObject.begin(), primeObject.end());
Alpana Kumari65b83602020-09-01 00:24:56 -05001171
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001172 // if system VPD has been restored at standby, update the EEPROM
1173 for (const auto& item : updatedEeproms)
1174 {
1175 updateHardware(get<0>(item), get<1>(item), get<2>(item),
1176 get<3>(item));
1177 }
Alpana Kumarif05effd2021-04-07 07:32:53 -05001178
1179 // set the U-boot environment variable for device-tree
1180 if constexpr (is_same<T, Parsed>::value)
1181 {
Santosh Puranike5f177a2022-01-24 20:14:46 +05301182 setDevTreeEnv(fs::path(getSystemsJson(vpdMap)).filename());
Alpana Kumarif05effd2021-04-07 07:32:53 -05001183 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301184 }
1185
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301186 // Notify PIM
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05001187 common::utility::callPIM(move(objects));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301188}
1189
1190int main(int argc, char** argv)
1191{
1192 int rc = 0;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001193 json js{};
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001194 Binary vpdVector{};
1195 string file{};
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001196 // map to hold additional data in case of logging pel
1197 PelAdditionalData additionalData{};
1198
1199 // this is needed to hold base fru inventory path in case there is ECC or
1200 // vpd exception while parsing the file
1201 std::string baseFruInventoryPath = {};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301202
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001203 // severity for PEL
1204 PelSeverity pelSeverity = PelSeverity::WARNING;
1205
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301206 try
1207 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301208 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
1209 "in DBUS"};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301210
1211 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
Alpana Kumari2f793042020-08-18 05:51:03 -05001212 ->required();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301213
1214 CLI11_PARSE(app, argc, argv);
1215
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001216 // PEL severity should be ERROR in case of any system VPD failure
1217 if (file == systemVpdFilePath)
1218 {
1219 pelSeverity = PelSeverity::ERROR;
1220 }
1221
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301222 auto jsonToParse = INVENTORY_JSON_DEFAULT;
1223
1224 // If the symlink exists, it means it has been setup for us, switch the
1225 // path
1226 if (fs::exists(INVENTORY_JSON_SYM_LINK))
1227 {
1228 jsonToParse = INVENTORY_JSON_SYM_LINK;
1229 }
1230
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301231 // Make sure that the file path we get is for a supported EEPROM
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301232 ifstream inventoryJson(jsonToParse);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001233 if (!inventoryJson)
1234 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001235 throw(VpdJsonException("Failed to access Json path", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001236 }
1237
1238 try
1239 {
1240 js = json::parse(inventoryJson);
1241 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001242 catch (const json::parse_error& ex)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001243 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001244 throw(VpdJsonException("Json parsing failed", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001245 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301246
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301247 // Do we have the mandatory "frus" section?
1248 if (js.find("frus") == js.end())
1249 {
1250 throw(VpdJsonException("FRUs section not found in JSON",
1251 jsonToParse));
1252 }
1253
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301254 // Check if it's a udev path - patterned as(/ahb/ahb:apb/ahb:apb:bus@)
1255 if (file.find("/ahb:apb") != string::npos)
1256 {
1257 // Translate udev path to a generic /sys/bus/.. file path.
1258 udevToGenericPath(file);
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301259
1260 if ((js["frus"].find(file) != js["frus"].end()) &&
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301261 (file == systemVpdFilePath))
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301262 {
1263 return 0;
1264 }
1265 }
1266
1267 if (file.empty())
1268 {
1269 cerr << "The EEPROM path <" << file << "> is not valid.";
1270 return 0;
1271 }
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301272 if (js["frus"].find(file) == js["frus"].end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301273 {
Santosh Puranik88edeb62020-03-02 12:00:09 +05301274 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301275 }
1276
Alpana Kumari2f793042020-08-18 05:51:03 -05001277 if (!fs::exists(file))
1278 {
1279 cout << "Device path: " << file
1280 << " does not exist. Spurious udev event? Exiting." << endl;
1281 return 0;
1282 }
1283
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001284 baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
Santosh Puranik85893752020-11-10 21:31:43 +05301285 // Check if we can read the VPD file based on the power state
Santosh Puranik27a5e952021-10-07 22:08:01 -05001286 // We skip reading VPD when the power is ON in two scenarios:
1287 // 1) The eeprom we are trying to read is that of the system VPD
1288 // 2) The JSON tells us that the FRU EEPROM cannot be read when
1289 // we are powered ON.
1290 if (js["frus"][file].at(0).value("powerOffOnly", false) ||
1291 (file == systemVpdFilePath))
Santosh Puranik85893752020-11-10 21:31:43 +05301292 {
1293 if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
1294 getPowerState())
1295 {
1296 cout << "This VPD cannot be read when power is ON" << endl;
1297 return 0;
1298 }
1299 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001300
Alpana Kumari2f793042020-08-18 05:51:03 -05001301 try
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301302 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001303 vpdVector = getVpdDataInVector(js, file);
PriyangaRamasamy33c61c22021-04-06 11:15:57 -05001304 ParserInterface* parser = ParserFactory::getParser(vpdVector);
Alpana Kumari2f793042020-08-18 05:51:03 -05001305 variant<KeywordVpdMap, Store> parseResult;
1306 parseResult = parser->parse();
SunnySrivastava19849a195542020-09-07 06:04:50 -05001307
Alpana Kumari2f793042020-08-18 05:51:03 -05001308 if (auto pVal = get_if<Store>(&parseResult))
1309 {
1310 populateDbus(pVal->getVpdMap(), js, file);
1311 }
1312 else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
1313 {
1314 populateDbus(*pVal, js, file);
1315 }
1316
1317 // release the parser object
1318 ParserFactory::freeParser(parser);
1319 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001320 catch (const exception& e)
Alpana Kumari2f793042020-08-18 05:51:03 -05001321 {
1322 postFailAction(js, file);
PriyangaRamasamya504c3e2020-12-06 12:14:52 -06001323 throw;
Alpana Kumari2f793042020-08-18 05:51:03 -05001324 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301325 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001326 catch (const VpdJsonException& ex)
1327 {
1328 additionalData.emplace("JSON_PATH", ex.getJsonPath());
1329 additionalData.emplace("DESCRIPTION", ex.what());
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001330 createPEL(additionalData, pelSeverity, errIntfForJsonFailure);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001331
1332 cerr << ex.what() << "\n";
1333 rc = -1;
1334 }
1335 catch (const VpdEccException& ex)
1336 {
1337 additionalData.emplace("DESCRIPTION", "ECC check failed");
1338 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1339 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001340 createPEL(additionalData, pelSeverity, errIntfForEccCheckFail);
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001341 dumpBadVpd(file, vpdVector);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001342 cerr << ex.what() << "\n";
1343 rc = -1;
1344 }
1345 catch (const VpdDataException& ex)
1346 {
1347 additionalData.emplace("DESCRIPTION", "Invalid VPD data");
1348 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1349 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001350 createPEL(additionalData, pelSeverity, errIntfForInvalidVPD);
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001351 dumpBadVpd(file, vpdVector);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001352 cerr << ex.what() << "\n";
1353 rc = -1;
1354 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001355 catch (const exception& e)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301356 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001357 dumpBadVpd(file, vpdVector);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301358 cerr << e.what() << "\n";
1359 rc = -1;
1360 }
1361
1362 return rc;
Alpana Kumari37e72702021-11-18 11:18:04 -06001363}