blob: 410e935df9adf044a6dd4fe3c7ed7e247620ef34 [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>
Andrew Geissler280197e2020-12-08 20:51:49 -060025#include <phosphor-logging/log.hpp>
alpana077ce68722021-07-25 13:23:59 -050026#include <regex>
Santosh Puranik253fbe92022-10-06 22:38:09 +053027#include <thread>
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 Puranike9c57532022-03-15 16:51:51 +053044 * @brief Returns the BMC state
45 */
46static auto getBMCState()
47{
48 std::string bmcState;
49 try
50 {
51 auto bus = sdbusplus::bus::new_default();
52 auto properties = bus.new_method_call(
53 "xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0",
54 "org.freedesktop.DBus.Properties", "Get");
55 properties.append("xyz.openbmc_project.State.BMC");
56 properties.append("CurrentBMCState");
57 auto result = bus.call(properties);
58 std::variant<std::string> val;
59 result.read(val);
60 if (auto pVal = std::get_if<std::string>(&val))
61 {
62 bmcState = *pVal;
63 }
64 }
65 catch (const sdbusplus::exception::SdBusError& e)
66 {
67 // Ignore any error
68 std::cerr << "Failed to get BMC state: " << e.what() << "\n";
69 }
70 return bmcState;
71}
72
73/**
74 * @brief Check if the FRU is in the cache
75 *
76 * Checks if the FRU associated with the supplied D-Bus object path is already
77 * on D-Bus. This can be used to test if a VPD collection is required for this
78 * FRU. It uses the "xyz.openbmc_project.Inventory.Item, Present" property to
79 * determine the presence of a FRU in the cache.
80 *
81 * @param objectPath - The D-Bus object path without the PIM prefix.
82 * @return true if the object exists on D-Bus, false otherwise.
83 */
84static auto isFruInVpdCache(const std::string& objectPath)
85{
86 try
87 {
88 auto bus = sdbusplus::bus::new_default();
89 auto invPath = std::string{pimPath} + objectPath;
90 auto props = bus.new_method_call(
91 "xyz.openbmc_project.Inventory.Manager", invPath.c_str(),
92 "org.freedesktop.DBus.Properties", "Get");
93 props.append("xyz.openbmc_project.Inventory.Item");
94 props.append("Present");
95 auto result = bus.call(props);
96 std::variant<bool> present;
97 result.read(present);
98 if (auto pVal = std::get_if<bool>(&present))
99 {
100 return *pVal;
101 }
102 return false;
103 }
104 catch (const sdbusplus::exception::SdBusError& e)
105 {
106 std::cout << "FRU: " << objectPath << " not in D-Bus\n";
107 // Assume not present in case of an error
108 return false;
109 }
110}
111
112/**
113 * @brief Check if VPD recollection is needed for the given EEPROM
114 *
115 * Not all FRUs can be swapped at BMC ready state. This function does the
116 * following:
117 * -- Check if the FRU is marked as "pluggableAtStandby" OR
118 * "concurrentlyMaintainable". If so, return true.
119 * -- Check if we are at BMC NotReady state. If we are, then return true.
120 * -- Else check if the FRU is not present in the VPD cache (to cover for VPD
121 * force collection). If not found in the cache, return true.
122 * -- Else return false.
123 *
124 * @param js - JSON Object.
125 * @param filePath - The EEPROM file.
126 * @return true if collection should be attempted, false otherwise.
127 */
128static auto needsRecollection(const nlohmann::json& js, const string& filePath)
129{
130 if (js["frus"][filePath].at(0).value("pluggableAtStandby", false) ||
131 js["frus"][filePath].at(0).value("concurrentlyMaintainable", false))
132 {
133 return true;
134 }
135 if (getBMCState() == "xyz.openbmc_project.State.BMC.BMCState.NotReady")
136 {
137 return true;
138 }
139 if (!isFruInVpdCache(js["frus"][filePath].at(0).value("inventoryPath", "")))
140 {
141 return true;
142 }
143 return false;
144}
145
146/**
Santosh Puranik88edeb62020-03-02 12:00:09 +0530147 * @brief Expands location codes
148 */
149static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
150 bool isSystemVpd)
151{
152 auto expanded{unexpanded};
153 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
154 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
155 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
156 size_t idx = expanded.find("fcs");
157 try
158 {
159 if (idx != string::npos)
160 {
161 string fc{};
162 string se{};
163 if (isSystemVpd)
164 {
165 const auto& fcData = vpdMap.at("VCEN").at("FC");
166 const auto& seData = vpdMap.at("VCEN").at("SE");
167 fc = string(fcData.data(), fcData.size());
168 se = string(seData.data(), seData.size());
169 }
170 else
171 {
172 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
173 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
174 }
175
Alpana Kumari81671f62021-02-10 02:21:59 -0600176 // TODO: See if ND0 can be placed in the JSON
177 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND0." + se);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530178 }
179 else
180 {
181 idx = expanded.find("mts");
182 if (idx != string::npos)
183 {
184 string mt{};
185 string se{};
186 if (isSystemVpd)
187 {
188 const auto& mtData = vpdMap.at("VSYS").at("TM");
189 const auto& seData = vpdMap.at("VSYS").at("SE");
190 mt = string(mtData.data(), mtData.size());
191 se = string(seData.data(), seData.size());
192 }
193 else
194 {
195 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
196 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
197 }
198
199 replace(mt.begin(), mt.end(), '-', '.');
200 expanded.replace(idx, 3, mt + "." + se);
201 }
202 }
203 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500204 catch (const exception& e)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530205 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500206 cerr << "Failed to expand location code with exception: " << e.what()
207 << "\n";
Santosh Puranik88edeb62020-03-02 12:00:09 +0530208 }
209 return expanded;
210}
Alpana Kumari2f793042020-08-18 05:51:03 -0500211
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530212/**
213 * @brief Populate FRU specific interfaces.
214 *
215 * This is a common method which handles both
216 * ipz and keyword specific interfaces thus,
217 * reducing the code redundancy.
218 * @param[in] map - Reference to the innermost keyword-value map.
219 * @param[in] preIntrStr - Reference to the interface string.
220 * @param[out] interfaces - Reference to interface map.
221 */
222template <typename T>
223static void populateFruSpecificInterfaces(const T& map,
224 const string& preIntrStr,
225 inventory::InterfaceMap& interfaces)
226{
227 inventory::PropertyMap prop;
228
229 for (const auto& kwVal : map)
230 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530231 auto kw = kwVal.first;
232
233 if (kw[0] == '#')
234 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500235 kw = string("PD_") + kw[1];
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530236 }
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500237 else if (isdigit(kw[0]))
238 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500239 kw = string("N_") + kw;
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500240 }
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000241 if constexpr (is_same<T, KeywordVpdMap>::value)
242 {
243 if (get_if<Binary>(&kwVal.second))
244 {
245 Binary vec(get_if<Binary>(&kwVal.second)->begin(),
246 get_if<Binary>(&kwVal.second)->end());
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000247 prop.emplace(move(kw), move(vec));
248 }
249 else
250 {
251 if (kw == "MemorySizeInKB")
252 {
253 inventory::PropertyMap memProp;
254 auto memVal = get_if<size_t>(&kwVal.second);
255 if (memVal)
256 {
257 memProp.emplace(move(kw),
258 ((*memVal) * CONVERT_MB_TO_KB));
259 interfaces.emplace(
260 "xyz.openbmc_project.Inventory.Item.Dimm",
261 move(memProp));
262 }
263 else
264 {
265 cerr << "MemorySizeInKB value not found in vpd map\n";
266 }
267 }
268 }
269 }
270 else
271 {
272 Binary vec(kwVal.second.begin(), kwVal.second.end());
273 prop.emplace(move(kw), move(vec));
274 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530275 }
276
277 interfaces.emplace(preIntrStr, move(prop));
278}
279
280/**
281 * @brief Populate Interfaces.
282 *
283 * This method populates common and extra interfaces to dbus.
284 * @param[in] js - json object
285 * @param[out] interfaces - Reference to interface map
286 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530287 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530288 */
289template <typename T>
290static void populateInterfaces(const nlohmann::json& js,
291 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530292 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530293{
294 for (const auto& ifs : js.items())
295 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530296 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530297 inventory::PropertyMap props;
298
299 for (const auto& itr : ifs.value().items())
300 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530301 const string& busProp = itr.key();
302
Alpana Kumari31970de2020-02-17 06:49:57 -0600303 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530304 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530305 props.emplace(busProp, itr.value().get<bool>());
306 }
307 else if (itr.value().is_string())
308 {
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600309 if (busProp == "LocationCode" && inf == IBM_LOCATION_CODE_INF)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530310 {
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600311 std::string prop;
312 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530313 {
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000314 // TODO deprecate the com.ibm interface later
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600315 prop = expandLocationCode(itr.value().get<string>(),
316 vpdMap, isSystemVpd);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530317 }
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600318 else if constexpr (is_same<T, KeywordVpdMap>::value)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530319 {
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600320 // Send empty Parsed object to expandLocationCode api.
321 prop = expandLocationCode(itr.value().get<string>(),
322 Parsed{}, false);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530323 }
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600324 props.emplace(busProp, prop);
325 interfaces.emplace(XYZ_LOCATION_CODE_INF, props);
326 interfaces.emplace(IBM_LOCATION_CODE_INF, props);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530327 }
328 else
329 {
330 props.emplace(busProp, itr.value().get<string>());
331 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530332 }
Santosh Puraniked609af2021-06-21 11:30:07 +0530333 else if (itr.value().is_array())
334 {
335 try
336 {
337 props.emplace(busProp, itr.value().get<Binary>());
338 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500339 catch (const nlohmann::detail::type_error& e)
Santosh Puraniked609af2021-06-21 11:30:07 +0530340 {
341 std::cerr << "Type exception: " << e.what() << "\n";
342 // Ignore any type errors
343 }
344 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600345 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530346 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600347 const string& rec = itr.value().value("recordName", "");
348 const string& kw = itr.value().value("keywordName", "");
349 const string& encoding = itr.value().value("encoding", "");
350
Alpana Kumari58e22142020-05-05 00:22:12 -0500351 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530352 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530353 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
354 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600355 {
356 auto encoded =
357 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530358 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600359 }
360 }
Alpana Kumari58e22142020-05-05 00:22:12 -0500361 else if constexpr (is_same<T, KeywordVpdMap>::value)
Alpana Kumari31970de2020-02-17 06:49:57 -0600362 {
363 if (!kw.empty() && vpdMap.count(kw))
364 {
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000365 auto kwValue = get_if<Binary>(&vpdMap.at(kw));
366 auto uintValue = get_if<size_t>(&vpdMap.at(kw));
367
368 if (kwValue)
369 {
370 auto prop =
371 string((*kwValue).begin(), (*kwValue).end());
372
373 auto encoded = encodeKeyword(prop, encoding);
374
375 props.emplace(busProp, encoded);
376 }
377 else if (uintValue)
378 {
379 props.emplace(busProp, *uintValue);
380 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600381 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530382 }
383 }
Matt Spinlerb1e64bb2021-09-08 09:57:48 -0500384 else if (itr.value().is_number())
385 {
386 // For now assume the value is a size_t. In the future it would
387 // be nice to come up with a way to get the type from the JSON.
388 props.emplace(busProp, itr.value().get<size_t>());
389 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530390 }
Priyanga Ramasamyaa8a8932022-01-27 09:12:41 -0600391 insertOrMerge(interfaces, inf, move(props));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530392 }
393}
394
alpana075cb3b1f2021-12-16 11:19:36 -0600395/**
396 * @brief This API checks if this FRU is pcie_devices. If yes then it further
397 * checks whether it is PASS1 planar.
398 */
399static bool isThisPcieOnPass1planar(const nlohmann::json& js,
400 const string& file)
401{
402 auto isThisPCIeDev = false;
403 auto isPASS1 = false;
404
405 // Check if it is a PCIE device
406 if (js["frus"].find(file) != js["frus"].end())
407 {
Santosh Puranikc03f3902022-04-14 10:58:26 +0530408 if ((js["frus"][file].at(0).find("extraInterfaces") !=
409 js["frus"][file].at(0).end()))
alpana075cb3b1f2021-12-16 11:19:36 -0600410 {
Santosh Puranikc03f3902022-04-14 10:58:26 +0530411 if (js["frus"][file].at(0)["extraInterfaces"].find(
alpana075cb3b1f2021-12-16 11:19:36 -0600412 "xyz.openbmc_project.Inventory.Item.PCIeDevice") !=
Santosh Puranikc03f3902022-04-14 10:58:26 +0530413 js["frus"][file].at(0)["extraInterfaces"].end())
alpana075cb3b1f2021-12-16 11:19:36 -0600414 {
415 isThisPCIeDev = true;
416 }
417 }
418 }
419
420 if (isThisPCIeDev)
421 {
Alpana Kumaria6181e22022-05-12 05:01:53 -0500422 // Collect HW version and SystemType to know if it is PASS1 planar.
alpana075cb3b1f2021-12-16 11:19:36 -0600423 auto bus = sdbusplus::bus::new_default();
Alpana Kumaria6181e22022-05-12 05:01:53 -0500424 auto property1 = bus.new_method_call(
alpana075cb3b1f2021-12-16 11:19:36 -0600425 INVENTORY_MANAGER_SERVICE,
426 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
427 "org.freedesktop.DBus.Properties", "Get");
Alpana Kumaria6181e22022-05-12 05:01:53 -0500428 property1.append("com.ibm.ipzvpd.VINI");
429 property1.append("HW");
430 auto result1 = bus.call(property1);
431 inventory::Value hwVal;
432 result1.read(hwVal);
alpana075cb3b1f2021-12-16 11:19:36 -0600433
Alpana Kumaria6181e22022-05-12 05:01:53 -0500434 // SystemType
435 auto property2 = bus.new_method_call(
436 INVENTORY_MANAGER_SERVICE,
437 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
438 "org.freedesktop.DBus.Properties", "Get");
439 property2.append("com.ibm.ipzvpd.VSBP");
440 property2.append("IM");
441 auto result2 = bus.call(property2);
442 inventory::Value imVal;
443 result2.read(imVal);
444
445 auto pVal1 = get_if<Binary>(&hwVal);
446 auto pVal2 = get_if<Binary>(&imVal);
447
448 if (pVal1 && pVal2)
alpana075cb3b1f2021-12-16 11:19:36 -0600449 {
Alpana Kumaria6181e22022-05-12 05:01:53 -0500450 auto hwVersion = *pVal1;
451 auto systemType = *pVal2;
452
453 // IM kw for Everest
454 Binary everestSystem{80, 00, 48, 00};
455
456 if (systemType == everestSystem)
457 {
458 if (hwVersion[1] < 21)
459 {
460 isPASS1 = true;
461 }
462 }
463 else if (hwVersion[1] < 2)
464 {
alpana075cb3b1f2021-12-16 11:19:36 -0600465 isPASS1 = true;
Alpana Kumaria6181e22022-05-12 05:01:53 -0500466 }
alpana075cb3b1f2021-12-16 11:19:36 -0600467 }
468 }
469
470 return (isThisPCIeDev && isPASS1);
471}
472
Alpana Kumari735dee92022-03-25 01:24:40 -0500473/** Performs any pre-action needed to get the FRU setup for collection.
Alpana Kumari2f793042020-08-18 05:51:03 -0500474 *
475 * @param[in] json - json object
476 * @param[in] file - eeprom file path
477 */
478static void preAction(const nlohmann::json& json, const string& file)
479{
Alpana Kumari735dee92022-03-25 01:24:40 -0500480 if ((json["frus"][file].at(0)).find("preAction") ==
Alpana Kumari2f793042020-08-18 05:51:03 -0500481 json["frus"][file].at(0).end())
482 {
Alpana Kumari735dee92022-03-25 01:24:40 -0500483 return;
Alpana Kumari2f793042020-08-18 05:51:03 -0500484 }
485
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500486 try
Alpana Kumari2f793042020-08-18 05:51:03 -0500487 {
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500488 if (executePreAction(json, file))
Alpana Kumari2f793042020-08-18 05:51:03 -0500489 {
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500490 if (json["frus"][file].at(0).find("devAddress") !=
491 json["frus"][file].at(0).end())
Alpana Kumari40d1c192022-03-09 21:16:02 -0600492 {
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500493 // Now bind the device
494 string bind = json["frus"][file].at(0).value("devAddress", "");
495 cout << "Binding device " << bind << endl;
496 string bindCmd = string("echo \"") + bind +
497 string("\" > /sys/bus/i2c/drivers/at24/bind");
498 cout << bindCmd << endl;
499 executeCmd(bindCmd);
500
501 // Check if device showed up (test for file)
502 if (!fs::exists(file))
503 {
504 cerr << "EEPROM " << file
505 << " does not exist. Take failure action" << endl;
506 // If not, then take failure postAction
507 executePostFailAction(json, file);
508 }
509 }
510 else
511 {
512 // missing required informations
513 cerr << "VPD inventory JSON missing basic informations of "
514 "preAction "
515 "for this FRU : ["
516 << file << "]. Executing executePostFailAction." << endl;
517
518 // Take failure postAction
Alpana Kumari40d1c192022-03-09 21:16:02 -0600519 executePostFailAction(json, file);
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500520 return;
Alpana Kumari40d1c192022-03-09 21:16:02 -0600521 }
522 }
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500523 }
524 catch (const GpioException& e)
525 {
526 PelAdditionalData additionalData{};
527 additionalData.emplace("DESCRIPTION", e.what());
528 createPEL(additionalData, PelSeverity::WARNING, errIntfForGpioError,
529 nullptr);
Alpana Kumari2f793042020-08-18 05:51:03 -0500530 }
Alpana Kumari2f793042020-08-18 05:51:03 -0500531}
532
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530533/**
Santosh Puranikf3e69682022-03-31 17:52:38 +0530534 * @brief Fills the Decorator.AssetTag property into the interfaces map
535 *
536 * This function should only be called in cases where we did not find a JSON
537 * symlink. A missing symlink in /var/lib will be considered as a factory reset
538 * and this function will be used to default the AssetTag property.
539 *
540 * @param interfaces A possibly pre-populated map of inetrfaces to properties.
541 * @param vpdMap A VPD map of the system VPD data.
542 */
543static void fillAssetTag(inventory::InterfaceMap& interfaces,
544 const Parsed& vpdMap)
545{
546 // Read the system serial number and MTM
547 // Default asset tag is Server-MTM-System Serial
548 inventory::Interface assetIntf{
549 "xyz.openbmc_project.Inventory.Decorator.AssetTag"};
550 inventory::PropertyMap assetTagProps;
551 std::string defaultAssetTag =
552 std::string{"Server-"} + getKwVal(vpdMap, "VSYS", "TM") +
553 std::string{"-"} + getKwVal(vpdMap, "VSYS", "SE");
554 assetTagProps.emplace("AssetTag", defaultAssetTag);
555 insertOrMerge(interfaces, assetIntf, std::move(assetTagProps));
556}
557
558/**
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530559 * @brief Set certain one time properties in the inventory
560 * Use this function to insert the Functional and Enabled properties into the
561 * inventory map. This function first checks if the object in question already
562 * has these properties hosted on D-Bus, if the property is already there, it is
563 * not modified, hence the name "one time". If the property is not already
564 * present, it will be added to the map with a suitable default value (true for
565 * Functional and false for Enabled)
566 *
567 * @param[in] object - The inventory D-Bus obejct without the inventory prefix.
568 * @param[inout] interfaces - Reference to a map of inventory interfaces to
569 * which the properties will be attached.
570 */
571static void setOneTimeProperties(const std::string& object,
572 inventory::InterfaceMap& interfaces)
573{
574 auto bus = sdbusplus::bus::new_default();
575 auto objectPath = INVENTORY_PATH + object;
576 auto prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
577 objectPath.c_str(),
578 "org.freedesktop.DBus.Properties", "Get");
579 prop.append("xyz.openbmc_project.State.Decorator.OperationalStatus");
580 prop.append("Functional");
581 try
582 {
583 auto result = bus.call(prop);
584 }
585 catch (const sdbusplus::exception::SdBusError& e)
586 {
587 // Treat as property unavailable
588 inventory::PropertyMap prop;
589 prop.emplace("Functional", true);
590 interfaces.emplace(
591 "xyz.openbmc_project.State.Decorator.OperationalStatus",
592 move(prop));
593 }
594 prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
595 objectPath.c_str(),
596 "org.freedesktop.DBus.Properties", "Get");
597 prop.append("xyz.openbmc_project.Object.Enable");
598 prop.append("Enabled");
599 try
600 {
601 auto result = bus.call(prop);
602 }
603 catch (const sdbusplus::exception::SdBusError& e)
604 {
605 // Treat as property unavailable
606 inventory::PropertyMap prop;
607 prop.emplace("Enabled", false);
608 interfaces.emplace("xyz.openbmc_project.Object.Enable", move(prop));
609 }
610}
611
612/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530613 * @brief Prime the Inventory
614 * Prime the inventory by populating only the location code,
615 * type interface and the inventory object for the frus
616 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530617 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530618 * @param[in] jsObject - Reference to vpd inventory json object
619 * @param[in] vpdMap - Reference to the parsed vpd map
620 *
621 * @returns Map of items in extraInterface.
622 */
623template <typename T>
624inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
625 const T& vpdMap)
626{
627 inventory::ObjectMap objects;
628
629 for (auto& itemFRUS : jsObject["frus"].items())
630 {
631 for (auto& itemEEPROM : itemFRUS.value())
632 {
Alpana Kumari2e6c6f72020-12-03 00:10:03 -0600633 // Take pre actions if needed
634 if (itemEEPROM.find("preAction") != itemEEPROM.end())
635 {
636 preAction(jsObject, itemFRUS.key());
637 }
638
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530639 inventory::InterfaceMap interfaces;
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530640 inventory::Object object(itemEEPROM.at("inventoryPath"));
641
Santosh Puranik50f60bf2021-05-26 17:55:06 +0530642 if ((itemFRUS.key() != systemVpdFilePath) &&
643 !itemEEPROM.value("noprime", false))
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530644 {
Alpana Kumaricfd7a752021-02-07 23:23:01 -0600645 inventory::PropertyMap presProp;
Priyanga Ramasamye358acb2022-03-21 14:21:50 -0500646
647 // Do not populate Present property for frus whose
648 // synthesized=true. synthesized=true says the fru is owned by
649 // some other component and not by vpd.
650 if (!itemEEPROM.value("synthesized", false))
651 {
652 presProp.emplace("Present", false);
653 interfaces.emplace("xyz.openbmc_project.Inventory.Item",
654 presProp);
655 }
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530656 setOneTimeProperties(object, interfaces);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530657 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
658 {
659 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
660 {
661 inventory::PropertyMap props;
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000662 if (eI.key() == IBM_LOCATION_CODE_INF)
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530663 {
664 if constexpr (std::is_same<T, Parsed>::value)
665 {
666 for (auto& lC : eI.value().items())
667 {
668 auto propVal = expandLocationCode(
669 lC.value().get<string>(), vpdMap, true);
670
671 props.emplace(move(lC.key()),
672 move(propVal));
Santosh Puranikb0f37492021-06-21 09:42:47 +0530673 interfaces.emplace(XYZ_LOCATION_CODE_INF,
674 props);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530675 interfaces.emplace(move(eI.key()),
676 move(props));
677 }
678 }
679 }
680 else if (eI.key().find("Inventory.Item.") !=
681 string::npos)
682 {
683 interfaces.emplace(move(eI.key()), move(props));
684 }
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530685 else if (eI.key() ==
686 "xyz.openbmc_project.Inventory.Item")
687 {
688 for (auto& val : eI.value().items())
689 {
690 if (val.key() == "PrettyName")
691 {
692 presProp.emplace(val.key(),
693 val.value().get<string>());
694 }
695 }
696 // Use insert_or_assign here as we may already have
697 // inserted the present property only earlier in
698 // this function under this same interface.
699 interfaces.insert_or_assign(eI.key(),
700 move(presProp));
701 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530702 }
703 }
704 objects.emplace(move(object), move(interfaces));
705 }
706 }
707 }
708 return objects;
709}
710
Alpana Kumari65b83602020-09-01 00:24:56 -0500711/**
712 * @brief This API executes command to set environment variable
713 * And then reboot the system
714 * @param[in] key -env key to set new value
715 * @param[in] value -value to set.
716 */
717void setEnvAndReboot(const string& key, const string& value)
718{
719 // set env and reboot and break.
720 executeCmd("/sbin/fw_setenv", key, value);
Andrew Geissler280197e2020-12-08 20:51:49 -0600721 log<level::INFO>("Rebooting BMC to pick up new device tree");
Alpana Kumari65b83602020-09-01 00:24:56 -0500722 // make dbus call to reboot
723 auto bus = sdbusplus::bus::new_default_system();
724 auto method = bus.new_method_call(
725 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
726 "org.freedesktop.systemd1.Manager", "Reboot");
727 bus.call_noreply(method);
728}
729
730/*
731 * @brief This API checks for env var fitconfig.
732 * If not initialised OR updated as per the current system type,
733 * update this env var and reboot the system.
734 *
735 * @param[in] systemType IM kwd in vpd tells about which system type it is.
736 * */
737void setDevTreeEnv(const string& systemType)
738{
Alpana Kumari37e72702021-11-18 11:18:04 -0600739 // Init with default dtb
740 string newDeviceTree = "conf-aspeed-bmc-ibm-rainier-p1.dtb";
Santosh Puranike5f177a2022-01-24 20:14:46 +0530741 static const deviceTreeMap deviceTreeSystemTypeMap = {
742 {RAINIER_2U, "conf-aspeed-bmc-ibm-rainier-p1.dtb"},
743 {RAINIER_2U_V2, "conf-aspeed-bmc-ibm-rainier.dtb"},
744 {RAINIER_4U, "conf-aspeed-bmc-ibm-rainier-4u-p1.dtb"},
745 {RAINIER_4U_V2, "conf-aspeed-bmc-ibm-rainier-4u.dtb"},
746 {RAINIER_1S4U, "conf-aspeed-bmc-ibm-rainier-1s4u.dtb"},
Alpana Kumari1b026112022-03-02 23:41:38 -0600747 {EVEREST, "conf-aspeed-bmc-ibm-everest.dtb"},
748 {EVEREST_V2, "conf-aspeed-bmc-ibm-everest.dtb"}};
Alpana Kumari65b83602020-09-01 00:24:56 -0500749
750 if (deviceTreeSystemTypeMap.find(systemType) !=
751 deviceTreeSystemTypeMap.end())
752 {
753 newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
754 }
Alpana Kumari37e72702021-11-18 11:18:04 -0600755 else
756 {
757 // System type not supported
Alpana Kumariab1e22c2021-11-24 11:03:38 -0600758 string err = "This System type not found/supported in dtb table " +
759 systemType +
760 ".Please check the HW and IM keywords in the system "
761 "VPD.Breaking...";
762
763 // map to hold additional data in case of logging pel
764 PelAdditionalData additionalData{};
765 additionalData.emplace("DESCRIPTION", err);
766 createPEL(additionalData, PelSeverity::WARNING,
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500767 errIntfForInvalidSystemType, nullptr);
Alpana Kumariab1e22c2021-11-24 11:03:38 -0600768 exit(-1);
Alpana Kumari37e72702021-11-18 11:18:04 -0600769 }
Alpana Kumari65b83602020-09-01 00:24:56 -0500770
771 string readVarValue;
772 bool envVarFound = false;
773
774 vector<string> output = executeCmd("/sbin/fw_printenv");
775 for (const auto& entry : output)
776 {
777 size_t pos = entry.find("=");
778 string key = entry.substr(0, pos);
779 if (key != "fitconfig")
780 {
781 continue;
782 }
783
784 envVarFound = true;
785 if (pos + 1 < entry.size())
786 {
787 readVarValue = entry.substr(pos + 1);
788 if (readVarValue.find(newDeviceTree) != string::npos)
789 {
790 // fitconfig is Updated. No action needed
791 break;
792 }
793 }
794 // set env and reboot and break.
795 setEnvAndReboot(key, newDeviceTree);
796 exit(0);
797 }
798
799 // check If env var Not found
800 if (!envVarFound)
801 {
802 setEnvAndReboot("fitconfig", newDeviceTree);
803 }
804}
805
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530806/**
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500807 * @brief API to check if we need to restore system VPD
808 * This functionality is only applicable for IPZ VPD data.
809 * @param[in] vpdMap - IPZ vpd map
810 * @param[in] objectPath - Object path for the FRU
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500811 */
Sunny Srivastava3c244142022-01-11 08:47:04 -0600812void restoreSystemVPD(Parsed& vpdMap, const string& objectPath)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500813{
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500814 for (const auto& systemRecKwdPair : svpdKwdMap)
815 {
816 auto it = vpdMap.find(systemRecKwdPair.first);
817
818 // check if record is found in map we got by parser
819 if (it != vpdMap.end())
820 {
821 const auto& kwdListForRecord = systemRecKwdPair.second;
822 for (const auto& keyword : kwdListForRecord)
823 {
824 DbusPropertyMap& kwdValMap = it->second;
825 auto iterator = kwdValMap.find(keyword);
826
827 if (iterator != kwdValMap.end())
828 {
829 string& kwdValue = iterator->second;
830
831 // check bus data
832 const string& recordName = systemRecKwdPair.first;
833 const string& busValue = readBusProperty(
834 objectPath, ipzVpdInf + recordName, keyword);
835
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -0500836 std::string defaultValue{' '};
837
838 // Explicit check for D0 is required as this keyword will
839 // never be blank and 0x00 should be treated as no value in
840 // this case.
841 if (recordName == "UTIL" && keyword == "D0")
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500842 {
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -0500843 // default value of kwd D0 is 0x00. This kwd will never
844 // be blank.
845 defaultValue = '\0';
846 }
847
848 if (busValue.find_first_not_of(defaultValue) !=
849 string::npos)
850 {
851 if (kwdValue.find_first_not_of(defaultValue) !=
852 string::npos)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500853 {
854 // both the data are present, check for mismatch
855 if (busValue != kwdValue)
856 {
857 string errMsg = "VPD data mismatch on cache "
858 "and hardware for record: ";
859 errMsg += (*it).first;
860 errMsg += " and keyword: ";
861 errMsg += keyword;
862
863 // data mismatch
864 PelAdditionalData additionalData;
865 additionalData.emplace("CALLOUT_INVENTORY_PATH",
866 objectPath);
867
868 additionalData.emplace("DESCRIPTION", errMsg);
869
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500870 createPEL(additionalData, PelSeverity::WARNING,
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500871 errIntfForInvalidVPD, nullptr);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500872 }
873 }
874 else
875 {
876 // implies hardware data is blank
877 // update the map
878 Binary busData(busValue.begin(), busValue.end());
879
Sunny Srivastava90a63b92021-05-26 06:30:24 -0500880 // update the map as well, so that cache data is not
881 // updated as blank while populating VPD map on Dbus
882 // in populateDBus Api
883 kwdValue = busValue;
884 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500885 }
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -0500886 else if (kwdValue.find_first_not_of(defaultValue) ==
887 string::npos)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500888 {
Priyanga Ramasamyda3b2d22022-10-31 05:42:22 -0500889 if (recordName == "VSYS" && keyword == "FV")
890 {
891 // Continue to the next keyword without logging +PEL
892 // for VSYS FV(stores min version of BMC firmware).
893 // Reason:There is a requirement to support blank FV
894 // so that customer can use the system without
895 // upgrading BMC to the minimum required version.
896 continue;
897 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500898 string errMsg = "VPD is blank on both cache and "
899 "hardware for record: ";
900 errMsg += (*it).first;
901 errMsg += " and keyword: ";
902 errMsg += keyword;
903 errMsg += ". SSR need to update hardware VPD.";
904
905 // both the data are blanks, log PEL
906 PelAdditionalData additionalData;
907 additionalData.emplace("CALLOUT_INVENTORY_PATH",
908 objectPath);
909
910 additionalData.emplace("DESCRIPTION", errMsg);
911
912 // log PEL TODO: Block IPL
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500913 createPEL(additionalData, PelSeverity::ERROR,
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500914 errIntfForBlankSystemVPD, nullptr);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500915 continue;
916 }
917 }
918 }
919 }
920 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500921}
922
923/**
alpana077ce68722021-07-25 13:23:59 -0500924 * @brief This checks for is this FRU a processor
925 * And if yes, then checks for is this primary
926 *
927 * @param[in] js- vpd json to get the information about this FRU
928 * @param[in] filePath- FRU vpd
929 *
930 * @return true/false
931 */
932bool isThisPrimaryProcessor(nlohmann::json& js, const string& filePath)
933{
934 bool isProcessor = false;
935 bool isPrimary = false;
936
937 for (const auto& item : js["frus"][filePath])
938 {
939 if (item.find("extraInterfaces") != item.end())
940 {
941 for (const auto& eI : item["extraInterfaces"].items())
942 {
943 if (eI.key().find("Inventory.Item.Cpu") != string::npos)
944 {
945 isProcessor = true;
946 }
947 }
948 }
949
950 if (isProcessor)
951 {
952 string cpuType = item.value("cpuType", "");
953 if (cpuType == "primary")
954 {
955 isPrimary = true;
956 }
957 }
958 }
959
960 return (isProcessor && isPrimary);
961}
962
963/**
964 * @brief This finds DIMM vpd in vpd json and enables them by binding the device
965 * driver
966 * @param[in] js- vpd json to iterate through and take action if it is DIMM
967 */
968void doEnableAllDimms(nlohmann::json& js)
969{
970 // iterate over each fru
971 for (const auto& eachFru : js["frus"].items())
972 {
973 // skip the driver binding if eeprom already exists
974 if (fs::exists(eachFru.key()))
975 {
976 continue;
977 }
978
979 for (const auto& eachInventory : eachFru.value())
980 {
981 if (eachInventory.find("extraInterfaces") != eachInventory.end())
982 {
983 for (const auto& eI : eachInventory["extraInterfaces"].items())
984 {
985 if (eI.key().find("Inventory.Item.Dimm") != string::npos)
986 {
987 string dimmVpd = eachFru.key();
988 // fetch it from
989 // "/sys/bus/i2c/drivers/at24/414-0050/eeprom"
990
991 regex matchPatern("([0-9]+-[0-9]{4})");
992 smatch matchFound;
993 if (regex_search(dimmVpd, matchFound, matchPatern))
994 {
995 vector<string> i2cReg;
996 boost::split(i2cReg, matchFound.str(0),
997 boost::is_any_of("-"));
998
999 // remove 0s from begining
1000 const regex pattern("^0+(?!$)");
1001 for (auto& i : i2cReg)
1002 {
1003 i = regex_replace(i, pattern, "");
1004 }
1005
1006 if (i2cReg.size() == 2)
1007 {
1008 // echo 24c32 0x50 >
1009 // /sys/bus/i2c/devices/i2c-16/new_device
1010 string cmnd = "echo 24c32 0x" + i2cReg[1] +
1011 " > /sys/bus/i2c/devices/i2c-" +
1012 i2cReg[0] + "/new_device";
1013
1014 executeCmd(cmnd);
1015 }
1016 }
1017 }
1018 }
1019 }
1020 }
1021 }
1022}
1023
1024/**
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001025 * @brief Check if the given CPU is an IO only chip.
1026 * The CPU is termed as IO, whose all of the cores are bad and can never be
1027 * used. Those CPU chips can be used for IO purpose like connecting PCIe devices
1028 * etc., The CPU whose every cores are bad, can be identified from the CP00
1029 * record's PG keyword, only if all of the 8 EQs' value equals 0xE7F9FF. (1EQ
1030 * has 4 cores grouped together by sharing its cache memory.)
1031 * @param [in] pgKeyword - PG Keyword of CPU.
1032 * @return true if the given cpu is an IO, false otherwise.
1033 */
1034static bool isCPUIOGoodOnly(const string& pgKeyword)
1035{
1036 const unsigned char io[] = {0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9,
1037 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7,
1038 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
1039 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
1040 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
1041 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
1042 // IO.
1043 if (memcmp(io, pgKeyword.data() + 97, 24) == 0)
1044 {
1045 return true;
1046 }
1047
1048 // The CPU is not an IO
1049 return false;
1050}
1051
1052/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301053 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301054 * This method invokes all the populateInterface functions
1055 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301056 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
1057 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301058 * @param[in] js - Inventory json object
1059 * @param[in] filePath - Path of the vpd file
1060 * @param[in] preIntrStr - Interface string
1061 */
1062template <typename T>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001063static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301064{
1065 inventory::InterfaceMap interfaces;
1066 inventory::ObjectMap objects;
1067 inventory::PropertyMap prop;
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001068 string ccinFromVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301069
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301070 bool isSystemVpd = (filePath == systemVpdFilePath);
1071 if constexpr (is_same<T, Parsed>::value)
1072 {
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001073 ccinFromVpd = getKwVal(vpdMap, "VINI", "CC");
1074 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1075 ::toupper);
1076
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301077 if (isSystemVpd)
1078 {
1079 std::vector<std::string> interfaces = {motherBoardInterface};
1080 // call mapper to check for object path creation
1081 MapperResponse subTree =
1082 getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
1083 string mboardPath =
1084 js["frus"][filePath].at(0).value("inventoryPath", "");
1085
1086 // Attempt system VPD restore if we have a motherboard
1087 // object in the inventory.
1088 if ((subTree.size() != 0) &&
1089 (subTree.find(pimPath + mboardPath) != subTree.end()))
1090 {
Sunny Srivastava3c244142022-01-11 08:47:04 -06001091 restoreSystemVPD(vpdMap, mboardPath);
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301092 }
1093 else
1094 {
1095 log<level::ERR>("No object path found");
1096 }
1097 }
alpana077ce68722021-07-25 13:23:59 -05001098 else
1099 {
1100 // check if it is processor vpd.
1101 auto isPrimaryCpu = isThisPrimaryProcessor(js, filePath);
1102
1103 if (isPrimaryCpu)
1104 {
1105 auto ddVersion = getKwVal(vpdMap, "CRP0", "DD");
1106
1107 auto chipVersion = atoi(ddVersion.substr(1, 2).c_str());
1108
1109 if (chipVersion >= 2)
1110 {
1111 doEnableAllDimms(js);
Santosh Puranik253fbe92022-10-06 22:38:09 +05301112 // Sleep for a few seconds to let the DIMM parses start
1113 using namespace std::chrono_literals;
1114 std::this_thread::sleep_for(5s);
alpana077ce68722021-07-25 13:23:59 -05001115 }
1116 }
1117 }
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301118 }
1119
Santosh Puranikf3e69682022-03-31 17:52:38 +05301120 auto processFactoryReset = false;
1121
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001122 if (isSystemVpd)
1123 {
1124 string systemJsonName{};
1125 if constexpr (is_same<T, Parsed>::value)
1126 {
1127 // pick the right system json
1128 systemJsonName = getSystemsJson(vpdMap);
1129 }
1130
1131 fs::path target = systemJsonName;
1132 fs::path link = INVENTORY_JSON_SYM_LINK;
1133
Santosh Puranikf3e69682022-03-31 17:52:38 +05301134 // If the symlink does not exist, we treat that as a factory reset
1135 processFactoryReset = !fs::exists(INVENTORY_JSON_SYM_LINK);
1136
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001137 // Create the directory for hosting the symlink
1138 fs::create_directories(VPD_FILES_PATH);
1139 // unlink the symlink previously created (if any)
1140 remove(INVENTORY_JSON_SYM_LINK);
1141 // create a new symlink based on the system
1142 fs::create_symlink(target, link);
1143
1144 // Reloading the json
1145 ifstream inventoryJson(link);
1146 js = json::parse(inventoryJson);
1147 inventoryJson.close();
1148 }
1149
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301150 for (const auto& item : js["frus"][filePath])
1151 {
1152 const auto& objectPath = item["inventoryPath"];
1153 sdbusplus::message::object_path object(objectPath);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001154
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001155 vector<string> ccinList;
1156 if (item.find("ccin") != item.end())
1157 {
1158 for (const auto& cc : item["ccin"])
1159 {
1160 string ccin = cc;
1161 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1162 ccinList.push_back(ccin);
1163 }
1164 }
1165
1166 if (!ccinFromVpd.empty() && !ccinList.empty() &&
1167 (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1168 ccinList.end()))
1169 {
1170 continue;
1171 }
1172
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001173 if ((isSystemVpd) || (item.value("noprime", false)))
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301174 {
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001175
1176 // Populate one time properties for the system VPD and its sub-frus
1177 // and for other non-primeable frus.
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301178 // For the remaining FRUs, this will get handled as a part of
1179 // priming the inventory.
1180 setOneTimeProperties(objectPath, interfaces);
1181 }
1182
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301183 // Populate the VPD keywords and the common interfaces only if we
1184 // are asked to inherit that data from the VPD, else only add the
1185 // extraInterfaces.
1186 if (item.value("inherit", true))
1187 {
Alpana Kumari58e22142020-05-05 00:22:12 -05001188 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301189 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301190 // Each record in the VPD becomes an interface and all
1191 // keyword within the record are properties under that
1192 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301193 for (const auto& record : vpdMap)
1194 {
1195 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001196 record.second, ipzVpdInf + record.first, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301197 }
1198 }
Alpana Kumari58e22142020-05-05 00:22:12 -05001199 else if constexpr (is_same<T, KeywordVpdMap>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301200 {
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001201 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301202 }
Santosh Puranik88edeb62020-03-02 12:00:09 +05301203 if (js.find("commonInterfaces") != js.end())
1204 {
1205 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
1206 isSystemVpd);
1207 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301208 }
Santosh Puranik0859eb62020-03-16 02:56:29 -05001209 else
1210 {
1211 // Check if we have been asked to inherit specific record(s)
Alpana Kumari58e22142020-05-05 00:22:12 -05001212 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik0859eb62020-03-16 02:56:29 -05001213 {
1214 if (item.find("copyRecords") != item.end())
1215 {
1216 for (const auto& record : item["copyRecords"])
1217 {
1218 const string& recordName = record;
1219 if (vpdMap.find(recordName) != vpdMap.end())
1220 {
1221 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001222 vpdMap.at(recordName), ipzVpdInf + recordName,
Santosh Puranik0859eb62020-03-16 02:56:29 -05001223 interfaces);
1224 }
1225 }
1226 }
1227 }
1228 }
Santosh Puranik32c46502022-02-10 08:55:07 +05301229 // Populate interfaces and properties that are common to every FRU
1230 // and additional interface that might be defined on a per-FRU
1231 // basis.
1232 if (item.find("extraInterfaces") != item.end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301233 {
Santosh Puranik32c46502022-02-10 08:55:07 +05301234 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
1235 isSystemVpd);
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001236 if constexpr (is_same<T, Parsed>::value)
1237 {
1238 if (item["extraInterfaces"].find(
1239 "xyz.openbmc_project.Inventory.Item.Cpu") !=
1240 item["extraInterfaces"].end())
1241 {
1242 if (isCPUIOGoodOnly(getKwVal(vpdMap, "CP00", "PG")))
1243 {
Priyanga Ramasamy2c607a92022-04-08 00:30:17 -05001244 interfaces[invItemIntf]["PrettyName"] = "IO Module";
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001245 }
1246 }
1247 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301248 }
Priyanga Ramasamye358acb2022-03-21 14:21:50 -05001249
1250 // embedded property(true or false) says whether the subfru is embedded
1251 // into the parent fru (or) not. VPD sets Present property only for
1252 // embedded frus. If the subfru is not an embedded FRU, the subfru may
1253 // or may not be physically present. Those non embedded frus will always
1254 // have Present=false irrespective of its physical presence or absence.
1255 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
1256 // Present to true for such sub frus.
1257 // Eg: ethernet port is embedded into bmc card. So set Present to true
1258 // for such sub frus. Also donot populate present property for embedded
1259 // subfru which is synthesized. Currently there is no subfru which are
1260 // both embedded and synthesized. But still the case is handled here.
1261 if ((item.value("embedded", true)) &&
1262 (!item.value("synthesized", false)))
1263 {
1264 inventory::PropertyMap presProp;
1265 presProp.emplace("Present", true);
1266 insertOrMerge(interfaces, invItemIntf, move(presProp));
1267 }
Priyanga Ramasamyaa8a8932022-01-27 09:12:41 -06001268
Santosh Puranikf3e69682022-03-31 17:52:38 +05301269 if constexpr (is_same<T, Parsed>::value)
1270 {
1271 // Restore asset tag, if needed
1272 if (processFactoryReset && objectPath == "/system")
1273 {
1274 fillAssetTag(interfaces, vpdMap);
1275 }
1276 }
1277
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301278 objects.emplace(move(object), move(interfaces));
1279 }
1280
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301281 if (isSystemVpd)
1282 {
1283 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
1284 objects.insert(primeObject.begin(), primeObject.end());
Alpana Kumari65b83602020-09-01 00:24:56 -05001285
Alpana Kumarif05effd2021-04-07 07:32:53 -05001286 // set the U-boot environment variable for device-tree
1287 if constexpr (is_same<T, Parsed>::value)
1288 {
Santosh Puranike5f177a2022-01-24 20:14:46 +05301289 setDevTreeEnv(fs::path(getSystemsJson(vpdMap)).filename());
Alpana Kumarif05effd2021-04-07 07:32:53 -05001290 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301291 }
1292
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301293 // Notify PIM
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05001294 common::utility::callPIM(move(objects));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301295}
1296
1297int main(int argc, char** argv)
1298{
1299 int rc = 0;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001300 json js{};
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001301 Binary vpdVector{};
1302 string file{};
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001303 // map to hold additional data in case of logging pel
1304 PelAdditionalData additionalData{};
1305
1306 // this is needed to hold base fru inventory path in case there is ECC or
1307 // vpd exception while parsing the file
1308 std::string baseFruInventoryPath = {};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301309
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001310 // severity for PEL
1311 PelSeverity pelSeverity = PelSeverity::WARNING;
1312
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301313 try
1314 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301315 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
1316 "in DBUS"};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301317
1318 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
Alpana Kumari2f793042020-08-18 05:51:03 -05001319 ->required();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301320
1321 CLI11_PARSE(app, argc, argv);
1322
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001323 // PEL severity should be ERROR in case of any system VPD failure
1324 if (file == systemVpdFilePath)
1325 {
1326 pelSeverity = PelSeverity::ERROR;
1327 }
1328
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301329 auto jsonToParse = INVENTORY_JSON_DEFAULT;
1330
1331 // If the symlink exists, it means it has been setup for us, switch the
1332 // path
1333 if (fs::exists(INVENTORY_JSON_SYM_LINK))
1334 {
1335 jsonToParse = INVENTORY_JSON_SYM_LINK;
1336 }
1337
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301338 // Make sure that the file path we get is for a supported EEPROM
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301339 ifstream inventoryJson(jsonToParse);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001340 if (!inventoryJson)
1341 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001342 throw(VpdJsonException("Failed to access Json path", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001343 }
1344
1345 try
1346 {
1347 js = json::parse(inventoryJson);
1348 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001349 catch (const json::parse_error& ex)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001350 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001351 throw(VpdJsonException("Json parsing failed", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001352 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301353
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301354 // Do we have the mandatory "frus" section?
1355 if (js.find("frus") == js.end())
1356 {
1357 throw(VpdJsonException("FRUs section not found in JSON",
1358 jsonToParse));
1359 }
1360
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301361 // Check if it's a udev path - patterned as(/ahb/ahb:apb/ahb:apb:bus@)
1362 if (file.find("/ahb:apb") != string::npos)
1363 {
1364 // Translate udev path to a generic /sys/bus/.. file path.
1365 udevToGenericPath(file);
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301366
1367 if ((js["frus"].find(file) != js["frus"].end()) &&
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301368 (file == systemVpdFilePath))
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301369 {
Santosh Puranik6b2b5372022-06-02 20:49:02 +05301370 // We have already collected system VPD, skip.
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301371 return 0;
1372 }
1373 }
1374
1375 if (file.empty())
1376 {
1377 cerr << "The EEPROM path <" << file << "> is not valid.";
1378 return 0;
1379 }
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301380 if (js["frus"].find(file) == js["frus"].end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301381 {
Santosh Puranik88edeb62020-03-02 12:00:09 +05301382 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301383 }
1384
Alpana Kumari2f793042020-08-18 05:51:03 -05001385 if (!fs::exists(file))
1386 {
1387 cout << "Device path: " << file
1388 << " does not exist. Spurious udev event? Exiting." << endl;
1389 return 0;
1390 }
1391
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001392 baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
Santosh Puranik85893752020-11-10 21:31:43 +05301393 // Check if we can read the VPD file based on the power state
Santosh Puranik27a5e952021-10-07 22:08:01 -05001394 // We skip reading VPD when the power is ON in two scenarios:
Santosh Puranik31d50fa2022-04-04 12:04:37 +05301395 // 1) The eeprom we are trying to read is that of the system VPD and the
1396 // JSON symlink is already setup (the symlink's existence tells us we
1397 // are not coming out of a factory reset)
1398 // 2) The JSON tells us that the FRU EEPROM cannot be
1399 // read when we are powered ON.
Santosh Puranik27a5e952021-10-07 22:08:01 -05001400 if (js["frus"][file].at(0).value("powerOffOnly", false) ||
Santosh Puranik31d50fa2022-04-04 12:04:37 +05301401 (file == systemVpdFilePath && fs::exists(INVENTORY_JSON_SYM_LINK)))
Santosh Puranik85893752020-11-10 21:31:43 +05301402 {
1403 if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
1404 getPowerState())
1405 {
1406 cout << "This VPD cannot be read when power is ON" << endl;
1407 return 0;
1408 }
1409 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001410
Santosh Puranike9c57532022-03-15 16:51:51 +05301411 // Check if this VPD should be recollected at all
1412 if (!needsRecollection(js, file))
1413 {
1414 cout << "Skip VPD recollection for: " << file << endl;
1415 return 0;
1416 }
1417
Alpana Kumari2f793042020-08-18 05:51:03 -05001418 try
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301419 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001420 vpdVector = getVpdDataInVector(js, file);
Sunny Srivastavaf31a91b2022-06-09 08:11:29 -05001421 ParserInterface* parser = ParserFactory::getParser(
1422 vpdVector, (pimPath + baseFruInventoryPath));
Alpana Kumari2f793042020-08-18 05:51:03 -05001423 variant<KeywordVpdMap, Store> parseResult;
1424 parseResult = parser->parse();
SunnySrivastava19849a195542020-09-07 06:04:50 -05001425
Alpana Kumari2f793042020-08-18 05:51:03 -05001426 if (auto pVal = get_if<Store>(&parseResult))
1427 {
1428 populateDbus(pVal->getVpdMap(), js, file);
1429 }
1430 else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
1431 {
1432 populateDbus(*pVal, js, file);
1433 }
1434
1435 // release the parser object
1436 ParserFactory::freeParser(parser);
1437 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001438 catch (const exception& e)
Alpana Kumari2f793042020-08-18 05:51:03 -05001439 {
Alpana Kumari735dee92022-03-25 01:24:40 -05001440 executePostFailAction(js, file);
PriyangaRamasamya504c3e2020-12-06 12:14:52 -06001441 throw;
Alpana Kumari2f793042020-08-18 05:51:03 -05001442 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301443 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001444 catch (const VpdJsonException& ex)
1445 {
1446 additionalData.emplace("JSON_PATH", ex.getJsonPath());
1447 additionalData.emplace("DESCRIPTION", ex.what());
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -05001448 createPEL(additionalData, pelSeverity, errIntfForJsonFailure, nullptr);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001449
1450 cerr << ex.what() << "\n";
1451 rc = -1;
1452 }
1453 catch (const VpdEccException& ex)
1454 {
1455 additionalData.emplace("DESCRIPTION", "ECC check failed");
1456 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1457 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -05001458 createPEL(additionalData, pelSeverity, errIntfForEccCheckFail, nullptr);
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001459 dumpBadVpd(file, vpdVector);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001460 cerr << ex.what() << "\n";
1461 rc = -1;
1462 }
1463 catch (const VpdDataException& ex)
1464 {
alpana075cb3b1f2021-12-16 11:19:36 -06001465 if (isThisPcieOnPass1planar(js, file))
1466 {
1467 cout << "Pcie_device [" << file
1468 << "]'s VPD is not valid on PASS1 planar.Ignoring.\n";
1469 rc = 0;
1470 }
Santosh Puranik53b38ed2022-04-10 23:15:22 +05301471 else if (!(isPresent(js, file).value_or(true)))
1472 {
1473 cout << "FRU at: " << file
1474 << " is not detected present. Ignore parser error.\n";
1475 rc = 0;
1476 }
alpana075cb3b1f2021-12-16 11:19:36 -06001477 else
1478 {
1479 string errorMsg =
1480 "VPD file is either empty or invalid. Parser failed for [";
1481 errorMsg += file;
1482 errorMsg += "], with error = " + std::string(ex.what());
1483
1484 additionalData.emplace("DESCRIPTION", errorMsg);
1485 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1486 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -05001487 createPEL(additionalData, pelSeverity, errIntfForInvalidVPD,
1488 nullptr);
alpana075cb3b1f2021-12-16 11:19:36 -06001489
1490 rc = -1;
1491 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001492 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001493 catch (const exception& e)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301494 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001495 dumpBadVpd(file, vpdVector);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301496 cerr << e.what() << "\n";
1497 rc = -1;
1498 }
1499
1500 return rc;
Alpana Kumari735dee92022-03-25 01:24:40 -05001501}