blob: fbbdc92f0c4c3e5567694dee375a5634aaee4b96 [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>
Santosh Puranik253fbe92022-10-06 22:38:09 +053028#include <thread>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053029
30using namespace std;
31using namespace openpower::vpd;
32using namespace CLI;
33using namespace vpd::keyword::parser;
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053034using namespace openpower::vpd::constants;
35namespace fs = filesystem;
36using json = nlohmann::json;
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050037using namespace openpower::vpd::parser::factory;
SunnySrivastava1984945a02d2020-05-06 01:55:41 -050038using namespace openpower::vpd::inventory;
Alpana Kumaria00936f2020-04-14 07:15:46 -050039using namespace openpower::vpd::memory::parser;
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050040using namespace openpower::vpd::parser::interface;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -050041using namespace openpower::vpd::exceptions;
Andrew Geissler280197e2020-12-08 20:51:49 -060042using namespace phosphor::logging;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053043
Santosh Puranik88edeb62020-03-02 12:00:09 +053044/**
Santosh Puranike9c57532022-03-15 16:51:51 +053045 * @brief Returns the BMC state
46 */
47static auto getBMCState()
48{
49 std::string bmcState;
50 try
51 {
52 auto bus = sdbusplus::bus::new_default();
53 auto properties = bus.new_method_call(
54 "xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0",
55 "org.freedesktop.DBus.Properties", "Get");
56 properties.append("xyz.openbmc_project.State.BMC");
57 properties.append("CurrentBMCState");
58 auto result = bus.call(properties);
59 std::variant<std::string> val;
60 result.read(val);
61 if (auto pVal = std::get_if<std::string>(&val))
62 {
63 bmcState = *pVal;
64 }
65 }
66 catch (const sdbusplus::exception::SdBusError& e)
67 {
68 // Ignore any error
69 std::cerr << "Failed to get BMC state: " << e.what() << "\n";
70 }
71 return bmcState;
72}
73
74/**
75 * @brief Check if the FRU is in the cache
76 *
77 * Checks if the FRU associated with the supplied D-Bus object path is already
78 * on D-Bus. This can be used to test if a VPD collection is required for this
79 * FRU. It uses the "xyz.openbmc_project.Inventory.Item, Present" property to
80 * determine the presence of a FRU in the cache.
81 *
82 * @param objectPath - The D-Bus object path without the PIM prefix.
83 * @return true if the object exists on D-Bus, false otherwise.
84 */
85static auto isFruInVpdCache(const std::string& objectPath)
86{
87 try
88 {
89 auto bus = sdbusplus::bus::new_default();
90 auto invPath = std::string{pimPath} + objectPath;
91 auto props = bus.new_method_call(
92 "xyz.openbmc_project.Inventory.Manager", invPath.c_str(),
93 "org.freedesktop.DBus.Properties", "Get");
94 props.append("xyz.openbmc_project.Inventory.Item");
95 props.append("Present");
96 auto result = bus.call(props);
97 std::variant<bool> present;
98 result.read(present);
99 if (auto pVal = std::get_if<bool>(&present))
100 {
101 return *pVal;
102 }
103 return false;
104 }
105 catch (const sdbusplus::exception::SdBusError& e)
106 {
107 std::cout << "FRU: " << objectPath << " not in D-Bus\n";
108 // Assume not present in case of an error
109 return false;
110 }
111}
112
113/**
114 * @brief Check if VPD recollection is needed for the given EEPROM
115 *
116 * Not all FRUs can be swapped at BMC ready state. This function does the
117 * following:
118 * -- Check if the FRU is marked as "pluggableAtStandby" OR
119 * "concurrentlyMaintainable". If so, return true.
120 * -- Check if we are at BMC NotReady state. If we are, then return true.
121 * -- Else check if the FRU is not present in the VPD cache (to cover for VPD
122 * force collection). If not found in the cache, return true.
123 * -- Else return false.
124 *
125 * @param js - JSON Object.
126 * @param filePath - The EEPROM file.
127 * @return true if collection should be attempted, false otherwise.
128 */
129static auto needsRecollection(const nlohmann::json& js, const string& filePath)
130{
131 if (js["frus"][filePath].at(0).value("pluggableAtStandby", false) ||
132 js["frus"][filePath].at(0).value("concurrentlyMaintainable", false))
133 {
134 return true;
135 }
136 if (getBMCState() == "xyz.openbmc_project.State.BMC.BMCState.NotReady")
137 {
138 return true;
139 }
140 if (!isFruInVpdCache(js["frus"][filePath].at(0).value("inventoryPath", "")))
141 {
142 return true;
143 }
144 return false;
145}
146
147/**
Santosh Puranik88edeb62020-03-02 12:00:09 +0530148 * @brief Expands location codes
149 */
150static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
151 bool isSystemVpd)
152{
153 auto expanded{unexpanded};
154 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
155 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
156 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
157 size_t idx = expanded.find("fcs");
158 try
159 {
160 if (idx != string::npos)
161 {
162 string fc{};
163 string se{};
164 if (isSystemVpd)
165 {
166 const auto& fcData = vpdMap.at("VCEN").at("FC");
167 const auto& seData = vpdMap.at("VCEN").at("SE");
168 fc = string(fcData.data(), fcData.size());
169 se = string(seData.data(), seData.size());
170 }
171 else
172 {
173 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
174 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
175 }
176
Alpana Kumari81671f62021-02-10 02:21:59 -0600177 // TODO: See if ND0 can be placed in the JSON
178 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND0." + se);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530179 }
180 else
181 {
182 idx = expanded.find("mts");
183 if (idx != string::npos)
184 {
185 string mt{};
186 string se{};
187 if (isSystemVpd)
188 {
189 const auto& mtData = vpdMap.at("VSYS").at("TM");
190 const auto& seData = vpdMap.at("VSYS").at("SE");
191 mt = string(mtData.data(), mtData.size());
192 se = string(seData.data(), seData.size());
193 }
194 else
195 {
196 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
197 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
198 }
199
200 replace(mt.begin(), mt.end(), '-', '.');
201 expanded.replace(idx, 3, mt + "." + se);
202 }
203 }
204 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500205 catch (const exception& e)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530206 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500207 cerr << "Failed to expand location code with exception: " << e.what()
208 << "\n";
Santosh Puranik88edeb62020-03-02 12:00:09 +0530209 }
210 return expanded;
211}
Alpana Kumari2f793042020-08-18 05:51:03 -0500212
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530213/**
214 * @brief Populate FRU specific interfaces.
215 *
216 * This is a common method which handles both
217 * ipz and keyword specific interfaces thus,
218 * reducing the code redundancy.
219 * @param[in] map - Reference to the innermost keyword-value map.
220 * @param[in] preIntrStr - Reference to the interface string.
221 * @param[out] interfaces - Reference to interface map.
222 */
223template <typename T>
224static void populateFruSpecificInterfaces(const T& map,
225 const string& preIntrStr,
226 inventory::InterfaceMap& interfaces)
227{
228 inventory::PropertyMap prop;
229
230 for (const auto& kwVal : map)
231 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530232 auto kw = kwVal.first;
233
234 if (kw[0] == '#')
235 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500236 kw = string("PD_") + kw[1];
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530237 }
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500238 else if (isdigit(kw[0]))
239 {
Alpana Kumari58e22142020-05-05 00:22:12 -0500240 kw = string("N_") + kw;
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500241 }
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000242 if constexpr (is_same<T, KeywordVpdMap>::value)
243 {
244 if (get_if<Binary>(&kwVal.second))
245 {
246 Binary vec(get_if<Binary>(&kwVal.second)->begin(),
247 get_if<Binary>(&kwVal.second)->end());
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000248 prop.emplace(move(kw), move(vec));
249 }
250 else
251 {
252 if (kw == "MemorySizeInKB")
253 {
254 inventory::PropertyMap memProp;
255 auto memVal = get_if<size_t>(&kwVal.second);
256 if (memVal)
257 {
258 memProp.emplace(move(kw),
259 ((*memVal) * CONVERT_MB_TO_KB));
260 interfaces.emplace(
261 "xyz.openbmc_project.Inventory.Item.Dimm",
262 move(memProp));
263 }
264 else
265 {
266 cerr << "MemorySizeInKB value not found in vpd map\n";
267 }
268 }
269 }
270 }
271 else
272 {
273 Binary vec(kwVal.second.begin(), kwVal.second.end());
274 prop.emplace(move(kw), move(vec));
275 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530276 }
277
278 interfaces.emplace(preIntrStr, move(prop));
279}
280
281/**
282 * @brief Populate Interfaces.
283 *
284 * This method populates common and extra interfaces to dbus.
285 * @param[in] js - json object
286 * @param[out] interfaces - Reference to interface map
287 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530288 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530289 */
290template <typename T>
291static void populateInterfaces(const nlohmann::json& js,
292 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530293 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530294{
295 for (const auto& ifs : js.items())
296 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530297 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530298 inventory::PropertyMap props;
299
300 for (const auto& itr : ifs.value().items())
301 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530302 const string& busProp = itr.key();
303
Alpana Kumari31970de2020-02-17 06:49:57 -0600304 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530305 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530306 props.emplace(busProp, itr.value().get<bool>());
307 }
308 else if (itr.value().is_string())
309 {
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600310 if (busProp == "LocationCode" && inf == IBM_LOCATION_CODE_INF)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530311 {
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600312 std::string prop;
313 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530314 {
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000315 // TODO deprecate the com.ibm interface later
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600316 prop = expandLocationCode(itr.value().get<string>(),
317 vpdMap, isSystemVpd);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530318 }
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600319 else if constexpr (is_same<T, KeywordVpdMap>::value)
Santosh Puranik88edeb62020-03-02 12:00:09 +0530320 {
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600321 // Send empty Parsed object to expandLocationCode api.
322 prop = expandLocationCode(itr.value().get<string>(),
323 Parsed{}, false);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530324 }
Priyanga Ramasamy0d61c582022-01-21 04:38:22 -0600325 props.emplace(busProp, prop);
326 interfaces.emplace(XYZ_LOCATION_CODE_INF, props);
327 interfaces.emplace(IBM_LOCATION_CODE_INF, props);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530328 }
329 else
330 {
331 props.emplace(busProp, itr.value().get<string>());
332 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530333 }
Santosh Puraniked609af2021-06-21 11:30:07 +0530334 else if (itr.value().is_array())
335 {
336 try
337 {
338 props.emplace(busProp, itr.value().get<Binary>());
339 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500340 catch (const nlohmann::detail::type_error& e)
Santosh Puraniked609af2021-06-21 11:30:07 +0530341 {
342 std::cerr << "Type exception: " << e.what() << "\n";
343 // Ignore any type errors
344 }
345 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600346 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530347 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600348 const string& rec = itr.value().value("recordName", "");
349 const string& kw = itr.value().value("keywordName", "");
350 const string& encoding = itr.value().value("encoding", "");
351
Alpana Kumari58e22142020-05-05 00:22:12 -0500352 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530353 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530354 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
355 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600356 {
357 auto encoded =
358 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530359 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600360 }
361 }
Alpana Kumari58e22142020-05-05 00:22:12 -0500362 else if constexpr (is_same<T, KeywordVpdMap>::value)
Alpana Kumari31970de2020-02-17 06:49:57 -0600363 {
364 if (!kw.empty() && vpdMap.count(kw))
365 {
Alpana Kumari3ab26a72021-04-05 19:09:19 +0000366 auto kwValue = get_if<Binary>(&vpdMap.at(kw));
367 auto uintValue = get_if<size_t>(&vpdMap.at(kw));
368
369 if (kwValue)
370 {
371 auto prop =
372 string((*kwValue).begin(), (*kwValue).end());
373
374 auto encoded = encodeKeyword(prop, encoding);
375
376 props.emplace(busProp, encoded);
377 }
378 else if (uintValue)
379 {
380 props.emplace(busProp, *uintValue);
381 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600382 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530383 }
384 }
Matt Spinlerb1e64bb2021-09-08 09:57:48 -0500385 else if (itr.value().is_number())
386 {
387 // For now assume the value is a size_t. In the future it would
388 // be nice to come up with a way to get the type from the JSON.
389 props.emplace(busProp, itr.value().get<size_t>());
390 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530391 }
Priyanga Ramasamyaa8a8932022-01-27 09:12:41 -0600392 insertOrMerge(interfaces, inf, move(props));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530393 }
394}
395
alpana075cb3b1f2021-12-16 11:19:36 -0600396/**
397 * @brief This API checks if this FRU is pcie_devices. If yes then it further
398 * checks whether it is PASS1 planar.
399 */
400static bool isThisPcieOnPass1planar(const nlohmann::json& js,
401 const string& file)
402{
403 auto isThisPCIeDev = false;
404 auto isPASS1 = false;
405
406 // Check if it is a PCIE device
407 if (js["frus"].find(file) != js["frus"].end())
408 {
Santosh Puranikc03f3902022-04-14 10:58:26 +0530409 if ((js["frus"][file].at(0).find("extraInterfaces") !=
410 js["frus"][file].at(0).end()))
alpana075cb3b1f2021-12-16 11:19:36 -0600411 {
Santosh Puranikc03f3902022-04-14 10:58:26 +0530412 if (js["frus"][file].at(0)["extraInterfaces"].find(
alpana075cb3b1f2021-12-16 11:19:36 -0600413 "xyz.openbmc_project.Inventory.Item.PCIeDevice") !=
Santosh Puranikc03f3902022-04-14 10:58:26 +0530414 js["frus"][file].at(0)["extraInterfaces"].end())
alpana075cb3b1f2021-12-16 11:19:36 -0600415 {
416 isThisPCIeDev = true;
417 }
418 }
419 }
420
421 if (isThisPCIeDev)
422 {
Alpana Kumaria6181e22022-05-12 05:01:53 -0500423 // Collect HW version and SystemType to know if it is PASS1 planar.
alpana075cb3b1f2021-12-16 11:19:36 -0600424 auto bus = sdbusplus::bus::new_default();
Alpana Kumaria6181e22022-05-12 05:01:53 -0500425 auto property1 = bus.new_method_call(
alpana075cb3b1f2021-12-16 11:19:36 -0600426 INVENTORY_MANAGER_SERVICE,
427 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
428 "org.freedesktop.DBus.Properties", "Get");
Alpana Kumaria6181e22022-05-12 05:01:53 -0500429 property1.append("com.ibm.ipzvpd.VINI");
430 property1.append("HW");
431 auto result1 = bus.call(property1);
432 inventory::Value hwVal;
433 result1.read(hwVal);
alpana075cb3b1f2021-12-16 11:19:36 -0600434
Alpana Kumaria6181e22022-05-12 05:01:53 -0500435 // SystemType
436 auto property2 = bus.new_method_call(
437 INVENTORY_MANAGER_SERVICE,
438 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
439 "org.freedesktop.DBus.Properties", "Get");
440 property2.append("com.ibm.ipzvpd.VSBP");
441 property2.append("IM");
442 auto result2 = bus.call(property2);
443 inventory::Value imVal;
444 result2.read(imVal);
445
446 auto pVal1 = get_if<Binary>(&hwVal);
447 auto pVal2 = get_if<Binary>(&imVal);
448
449 if (pVal1 && pVal2)
alpana075cb3b1f2021-12-16 11:19:36 -0600450 {
Alpana Kumaria6181e22022-05-12 05:01:53 -0500451 auto hwVersion = *pVal1;
452 auto systemType = *pVal2;
453
454 // IM kw for Everest
455 Binary everestSystem{80, 00, 48, 00};
456
457 if (systemType == everestSystem)
458 {
459 if (hwVersion[1] < 21)
460 {
461 isPASS1 = true;
462 }
463 }
464 else if (hwVersion[1] < 2)
465 {
alpana075cb3b1f2021-12-16 11:19:36 -0600466 isPASS1 = true;
Alpana Kumaria6181e22022-05-12 05:01:53 -0500467 }
alpana075cb3b1f2021-12-16 11:19:36 -0600468 }
469 }
470
471 return (isThisPCIeDev && isPASS1);
472}
473
Alpana Kumari735dee92022-03-25 01:24:40 -0500474/** Performs any pre-action needed to get the FRU setup for collection.
Alpana Kumari2f793042020-08-18 05:51:03 -0500475 *
476 * @param[in] json - json object
477 * @param[in] file - eeprom file path
478 */
479static void preAction(const nlohmann::json& json, const string& file)
480{
Alpana Kumari735dee92022-03-25 01:24:40 -0500481 if ((json["frus"][file].at(0)).find("preAction") ==
Alpana Kumari2f793042020-08-18 05:51:03 -0500482 json["frus"][file].at(0).end())
483 {
Alpana Kumari735dee92022-03-25 01:24:40 -0500484 return;
Alpana Kumari2f793042020-08-18 05:51:03 -0500485 }
486
Alpana Kumari735dee92022-03-25 01:24:40 -0500487 if (executePreAction(json, file))
Alpana Kumari2f793042020-08-18 05:51:03 -0500488 {
Alpana Kumari735dee92022-03-25 01:24:40 -0500489 if (json["frus"][file].at(0).find("devAddress") !=
490 json["frus"][file].at(0).end())
Alpana Kumari2f793042020-08-18 05:51:03 -0500491 {
Alpana Kumari735dee92022-03-25 01:24:40 -0500492 // Now bind the device
493 string bind = json["frus"][file].at(0).value("devAddress", "");
494 cout << "Binding device " << bind << endl;
495 string bindCmd = string("echo \"") + bind +
496 string("\" > /sys/bus/i2c/drivers/at24/bind");
497 cout << bindCmd << endl;
498 executeCmd(bindCmd);
Alpana Kumari2e6c6f72020-12-03 00:10:03 -0600499
Alpana Kumari40d1c192022-03-09 21:16:02 -0600500 // Check if device showed up (test for file)
501 if (!fs::exists(file))
502 {
503 cerr << "EEPROM " << file
504 << " does not exist. Take failure action" << endl;
505 // If not, then take failure postAction
506 executePostFailAction(json, file);
507 }
508 }
509 else
Alpana Kumari735dee92022-03-25 01:24:40 -0500510 {
Alpana Kumari40d1c192022-03-09 21:16:02 -0600511 // missing required informations
512 cerr
513 << "VPD inventory JSON missing basic informations of preAction "
514 "for this FRU : ["
515 << file << "]. Executing executePostFailAction." << endl;
516
517 // Take failure postAction
Alpana Kumari735dee92022-03-25 01:24:40 -0500518 executePostFailAction(json, file);
Alpana Kumari40d1c192022-03-09 21:16:02 -0600519 return;
Alpana Kumari2f793042020-08-18 05:51:03 -0500520 }
521 }
Alpana Kumari2f793042020-08-18 05:51:03 -0500522}
523
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530524/**
Santosh Puranikf3e69682022-03-31 17:52:38 +0530525 * @brief Fills the Decorator.AssetTag property into the interfaces map
526 *
527 * This function should only be called in cases where we did not find a JSON
528 * symlink. A missing symlink in /var/lib will be considered as a factory reset
529 * and this function will be used to default the AssetTag property.
530 *
531 * @param interfaces A possibly pre-populated map of inetrfaces to properties.
532 * @param vpdMap A VPD map of the system VPD data.
533 */
534static void fillAssetTag(inventory::InterfaceMap& interfaces,
535 const Parsed& vpdMap)
536{
537 // Read the system serial number and MTM
538 // Default asset tag is Server-MTM-System Serial
539 inventory::Interface assetIntf{
540 "xyz.openbmc_project.Inventory.Decorator.AssetTag"};
541 inventory::PropertyMap assetTagProps;
542 std::string defaultAssetTag =
543 std::string{"Server-"} + getKwVal(vpdMap, "VSYS", "TM") +
544 std::string{"-"} + getKwVal(vpdMap, "VSYS", "SE");
545 assetTagProps.emplace("AssetTag", defaultAssetTag);
546 insertOrMerge(interfaces, assetIntf, std::move(assetTagProps));
547}
548
549/**
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530550 * @brief Set certain one time properties in the inventory
551 * Use this function to insert the Functional and Enabled properties into the
552 * inventory map. This function first checks if the object in question already
553 * has these properties hosted on D-Bus, if the property is already there, it is
554 * not modified, hence the name "one time". If the property is not already
555 * present, it will be added to the map with a suitable default value (true for
556 * Functional and false for Enabled)
557 *
558 * @param[in] object - The inventory D-Bus obejct without the inventory prefix.
559 * @param[inout] interfaces - Reference to a map of inventory interfaces to
560 * which the properties will be attached.
561 */
562static void setOneTimeProperties(const std::string& object,
563 inventory::InterfaceMap& interfaces)
564{
565 auto bus = sdbusplus::bus::new_default();
566 auto objectPath = INVENTORY_PATH + object;
567 auto prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
568 objectPath.c_str(),
569 "org.freedesktop.DBus.Properties", "Get");
570 prop.append("xyz.openbmc_project.State.Decorator.OperationalStatus");
571 prop.append("Functional");
572 try
573 {
574 auto result = bus.call(prop);
575 }
576 catch (const sdbusplus::exception::SdBusError& e)
577 {
578 // Treat as property unavailable
579 inventory::PropertyMap prop;
580 prop.emplace("Functional", true);
581 interfaces.emplace(
582 "xyz.openbmc_project.State.Decorator.OperationalStatus",
583 move(prop));
584 }
585 prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
586 objectPath.c_str(),
587 "org.freedesktop.DBus.Properties", "Get");
588 prop.append("xyz.openbmc_project.Object.Enable");
589 prop.append("Enabled");
590 try
591 {
592 auto result = bus.call(prop);
593 }
594 catch (const sdbusplus::exception::SdBusError& e)
595 {
596 // Treat as property unavailable
597 inventory::PropertyMap prop;
598 prop.emplace("Enabled", false);
599 interfaces.emplace("xyz.openbmc_project.Object.Enable", move(prop));
600 }
601}
602
603/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530604 * @brief Prime the Inventory
605 * Prime the inventory by populating only the location code,
606 * type interface and the inventory object for the frus
607 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530608 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530609 * @param[in] jsObject - Reference to vpd inventory json object
610 * @param[in] vpdMap - Reference to the parsed vpd map
611 *
612 * @returns Map of items in extraInterface.
613 */
614template <typename T>
615inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
616 const T& vpdMap)
617{
618 inventory::ObjectMap objects;
619
620 for (auto& itemFRUS : jsObject["frus"].items())
621 {
622 for (auto& itemEEPROM : itemFRUS.value())
623 {
Alpana Kumari2e6c6f72020-12-03 00:10:03 -0600624 // Take pre actions if needed
625 if (itemEEPROM.find("preAction") != itemEEPROM.end())
626 {
627 preAction(jsObject, itemFRUS.key());
628 }
629
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530630 inventory::InterfaceMap interfaces;
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530631 inventory::Object object(itemEEPROM.at("inventoryPath"));
632
Santosh Puranik50f60bf2021-05-26 17:55:06 +0530633 if ((itemFRUS.key() != systemVpdFilePath) &&
634 !itemEEPROM.value("noprime", false))
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530635 {
Alpana Kumaricfd7a752021-02-07 23:23:01 -0600636 inventory::PropertyMap presProp;
Priyanga Ramasamye358acb2022-03-21 14:21:50 -0500637
638 // Do not populate Present property for frus whose
639 // synthesized=true. synthesized=true says the fru is owned by
640 // some other component and not by vpd.
641 if (!itemEEPROM.value("synthesized", false))
642 {
643 presProp.emplace("Present", false);
644 interfaces.emplace("xyz.openbmc_project.Inventory.Item",
645 presProp);
646 }
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530647 setOneTimeProperties(object, interfaces);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530648 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
649 {
650 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
651 {
652 inventory::PropertyMap props;
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000653 if (eI.key() == IBM_LOCATION_CODE_INF)
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530654 {
655 if constexpr (std::is_same<T, Parsed>::value)
656 {
657 for (auto& lC : eI.value().items())
658 {
659 auto propVal = expandLocationCode(
660 lC.value().get<string>(), vpdMap, true);
661
662 props.emplace(move(lC.key()),
663 move(propVal));
Santosh Puranikb0f37492021-06-21 09:42:47 +0530664 interfaces.emplace(XYZ_LOCATION_CODE_INF,
665 props);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530666 interfaces.emplace(move(eI.key()),
667 move(props));
668 }
669 }
670 }
671 else if (eI.key().find("Inventory.Item.") !=
672 string::npos)
673 {
674 interfaces.emplace(move(eI.key()), move(props));
675 }
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530676 else if (eI.key() ==
677 "xyz.openbmc_project.Inventory.Item")
678 {
679 for (auto& val : eI.value().items())
680 {
681 if (val.key() == "PrettyName")
682 {
683 presProp.emplace(val.key(),
684 val.value().get<string>());
685 }
686 }
687 // Use insert_or_assign here as we may already have
688 // inserted the present property only earlier in
689 // this function under this same interface.
690 interfaces.insert_or_assign(eI.key(),
691 move(presProp));
692 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530693 }
694 }
695 objects.emplace(move(object), move(interfaces));
696 }
697 }
698 }
699 return objects;
700}
701
Alpana Kumari65b83602020-09-01 00:24:56 -0500702/**
703 * @brief This API executes command to set environment variable
704 * And then reboot the system
705 * @param[in] key -env key to set new value
706 * @param[in] value -value to set.
707 */
708void setEnvAndReboot(const string& key, const string& value)
709{
710 // set env and reboot and break.
711 executeCmd("/sbin/fw_setenv", key, value);
Andrew Geissler280197e2020-12-08 20:51:49 -0600712 log<level::INFO>("Rebooting BMC to pick up new device tree");
Alpana Kumari65b83602020-09-01 00:24:56 -0500713 // make dbus call to reboot
714 auto bus = sdbusplus::bus::new_default_system();
715 auto method = bus.new_method_call(
716 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
717 "org.freedesktop.systemd1.Manager", "Reboot");
718 bus.call_noreply(method);
719}
720
721/*
722 * @brief This API checks for env var fitconfig.
723 * If not initialised OR updated as per the current system type,
724 * update this env var and reboot the system.
725 *
726 * @param[in] systemType IM kwd in vpd tells about which system type it is.
727 * */
728void setDevTreeEnv(const string& systemType)
729{
Alpana Kumari37e72702021-11-18 11:18:04 -0600730 // Init with default dtb
731 string newDeviceTree = "conf-aspeed-bmc-ibm-rainier-p1.dtb";
Santosh Puranike5f177a2022-01-24 20:14:46 +0530732 static const deviceTreeMap deviceTreeSystemTypeMap = {
733 {RAINIER_2U, "conf-aspeed-bmc-ibm-rainier-p1.dtb"},
734 {RAINIER_2U_V2, "conf-aspeed-bmc-ibm-rainier.dtb"},
735 {RAINIER_4U, "conf-aspeed-bmc-ibm-rainier-4u-p1.dtb"},
736 {RAINIER_4U_V2, "conf-aspeed-bmc-ibm-rainier-4u.dtb"},
737 {RAINIER_1S4U, "conf-aspeed-bmc-ibm-rainier-1s4u.dtb"},
Alpana Kumari1b026112022-03-02 23:41:38 -0600738 {EVEREST, "conf-aspeed-bmc-ibm-everest.dtb"},
739 {EVEREST_V2, "conf-aspeed-bmc-ibm-everest.dtb"}};
Alpana Kumari65b83602020-09-01 00:24:56 -0500740
741 if (deviceTreeSystemTypeMap.find(systemType) !=
742 deviceTreeSystemTypeMap.end())
743 {
744 newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
745 }
Alpana Kumari37e72702021-11-18 11:18:04 -0600746 else
747 {
748 // System type not supported
Alpana Kumariab1e22c2021-11-24 11:03:38 -0600749 string err = "This System type not found/supported in dtb table " +
750 systemType +
751 ".Please check the HW and IM keywords in the system "
752 "VPD.Breaking...";
753
754 // map to hold additional data in case of logging pel
755 PelAdditionalData additionalData{};
756 additionalData.emplace("DESCRIPTION", err);
757 createPEL(additionalData, PelSeverity::WARNING,
758 errIntfForInvalidSystemType);
759 exit(-1);
Alpana Kumari37e72702021-11-18 11:18:04 -0600760 }
Alpana Kumari65b83602020-09-01 00:24:56 -0500761
762 string readVarValue;
763 bool envVarFound = false;
764
765 vector<string> output = executeCmd("/sbin/fw_printenv");
766 for (const auto& entry : output)
767 {
768 size_t pos = entry.find("=");
769 string key = entry.substr(0, pos);
770 if (key != "fitconfig")
771 {
772 continue;
773 }
774
775 envVarFound = true;
776 if (pos + 1 < entry.size())
777 {
778 readVarValue = entry.substr(pos + 1);
779 if (readVarValue.find(newDeviceTree) != string::npos)
780 {
781 // fitconfig is Updated. No action needed
782 break;
783 }
784 }
785 // set env and reboot and break.
786 setEnvAndReboot(key, newDeviceTree);
787 exit(0);
788 }
789
790 // check If env var Not found
791 if (!envVarFound)
792 {
793 setEnvAndReboot("fitconfig", newDeviceTree);
794 }
795}
796
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530797/**
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500798 * @brief API to check if we need to restore system VPD
799 * This functionality is only applicable for IPZ VPD data.
800 * @param[in] vpdMap - IPZ vpd map
801 * @param[in] objectPath - Object path for the FRU
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500802 */
Sunny Srivastava3c244142022-01-11 08:47:04 -0600803void restoreSystemVPD(Parsed& vpdMap, const string& objectPath)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500804{
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500805 for (const auto& systemRecKwdPair : svpdKwdMap)
806 {
807 auto it = vpdMap.find(systemRecKwdPair.first);
808
809 // check if record is found in map we got by parser
810 if (it != vpdMap.end())
811 {
812 const auto& kwdListForRecord = systemRecKwdPair.second;
813 for (const auto& keyword : kwdListForRecord)
814 {
815 DbusPropertyMap& kwdValMap = it->second;
816 auto iterator = kwdValMap.find(keyword);
817
818 if (iterator != kwdValMap.end())
819 {
820 string& kwdValue = iterator->second;
821
822 // check bus data
823 const string& recordName = systemRecKwdPair.first;
824 const string& busValue = readBusProperty(
825 objectPath, ipzVpdInf + recordName, keyword);
826
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -0500827 std::string defaultValue{' '};
828
829 // Explicit check for D0 is required as this keyword will
830 // never be blank and 0x00 should be treated as no value in
831 // this case.
832 if (recordName == "UTIL" && keyword == "D0")
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500833 {
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -0500834 // default value of kwd D0 is 0x00. This kwd will never
835 // be blank.
836 defaultValue = '\0';
837 }
838
839 if (busValue.find_first_not_of(defaultValue) !=
840 string::npos)
841 {
842 if (kwdValue.find_first_not_of(defaultValue) !=
843 string::npos)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500844 {
845 // both the data are present, check for mismatch
846 if (busValue != kwdValue)
847 {
848 string errMsg = "VPD data mismatch on cache "
849 "and hardware for record: ";
850 errMsg += (*it).first;
851 errMsg += " and keyword: ";
852 errMsg += keyword;
853
854 // data mismatch
855 PelAdditionalData additionalData;
856 additionalData.emplace("CALLOUT_INVENTORY_PATH",
857 objectPath);
858
859 additionalData.emplace("DESCRIPTION", errMsg);
860
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500861 createPEL(additionalData, PelSeverity::WARNING,
862 errIntfForInvalidVPD);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500863 }
864 }
865 else
866 {
867 // implies hardware data is blank
868 // update the map
869 Binary busData(busValue.begin(), busValue.end());
870
Sunny Srivastava90a63b92021-05-26 06:30:24 -0500871 // update the map as well, so that cache data is not
872 // updated as blank while populating VPD map on Dbus
873 // in populateDBus Api
874 kwdValue = busValue;
875 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500876 }
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -0500877 else if (kwdValue.find_first_not_of(defaultValue) ==
878 string::npos)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500879 {
880 string errMsg = "VPD is blank on both cache and "
881 "hardware for record: ";
882 errMsg += (*it).first;
883 errMsg += " and keyword: ";
884 errMsg += keyword;
885 errMsg += ". SSR need to update hardware VPD.";
886
887 // both the data are blanks, log PEL
888 PelAdditionalData additionalData;
889 additionalData.emplace("CALLOUT_INVENTORY_PATH",
890 objectPath);
891
892 additionalData.emplace("DESCRIPTION", errMsg);
893
894 // log PEL TODO: Block IPL
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500895 createPEL(additionalData, PelSeverity::ERROR,
896 errIntfForBlankSystemVPD);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500897 continue;
898 }
899 }
900 }
901 }
902 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500903}
904
905/**
alpana077ce68722021-07-25 13:23:59 -0500906 * @brief This checks for is this FRU a processor
907 * And if yes, then checks for is this primary
908 *
909 * @param[in] js- vpd json to get the information about this FRU
910 * @param[in] filePath- FRU vpd
911 *
912 * @return true/false
913 */
914bool isThisPrimaryProcessor(nlohmann::json& js, const string& filePath)
915{
916 bool isProcessor = false;
917 bool isPrimary = false;
918
919 for (const auto& item : js["frus"][filePath])
920 {
921 if (item.find("extraInterfaces") != item.end())
922 {
923 for (const auto& eI : item["extraInterfaces"].items())
924 {
925 if (eI.key().find("Inventory.Item.Cpu") != string::npos)
926 {
927 isProcessor = true;
928 }
929 }
930 }
931
932 if (isProcessor)
933 {
934 string cpuType = item.value("cpuType", "");
935 if (cpuType == "primary")
936 {
937 isPrimary = true;
938 }
939 }
940 }
941
942 return (isProcessor && isPrimary);
943}
944
945/**
946 * @brief This finds DIMM vpd in vpd json and enables them by binding the device
947 * driver
948 * @param[in] js- vpd json to iterate through and take action if it is DIMM
949 */
950void doEnableAllDimms(nlohmann::json& js)
951{
952 // iterate over each fru
953 for (const auto& eachFru : js["frus"].items())
954 {
955 // skip the driver binding if eeprom already exists
956 if (fs::exists(eachFru.key()))
957 {
958 continue;
959 }
960
961 for (const auto& eachInventory : eachFru.value())
962 {
963 if (eachInventory.find("extraInterfaces") != eachInventory.end())
964 {
965 for (const auto& eI : eachInventory["extraInterfaces"].items())
966 {
967 if (eI.key().find("Inventory.Item.Dimm") != string::npos)
968 {
969 string dimmVpd = eachFru.key();
970 // fetch it from
971 // "/sys/bus/i2c/drivers/at24/414-0050/eeprom"
972
973 regex matchPatern("([0-9]+-[0-9]{4})");
974 smatch matchFound;
975 if (regex_search(dimmVpd, matchFound, matchPatern))
976 {
977 vector<string> i2cReg;
978 boost::split(i2cReg, matchFound.str(0),
979 boost::is_any_of("-"));
980
981 // remove 0s from begining
982 const regex pattern("^0+(?!$)");
983 for (auto& i : i2cReg)
984 {
985 i = regex_replace(i, pattern, "");
986 }
987
988 if (i2cReg.size() == 2)
989 {
990 // echo 24c32 0x50 >
991 // /sys/bus/i2c/devices/i2c-16/new_device
992 string cmnd = "echo 24c32 0x" + i2cReg[1] +
993 " > /sys/bus/i2c/devices/i2c-" +
994 i2cReg[0] + "/new_device";
995
996 executeCmd(cmnd);
997 }
998 }
999 }
1000 }
1001 }
1002 }
1003 }
1004}
1005
1006/**
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001007 * @brief Check if the given CPU is an IO only chip.
1008 * The CPU is termed as IO, whose all of the cores are bad and can never be
1009 * used. Those CPU chips can be used for IO purpose like connecting PCIe devices
1010 * etc., The CPU whose every cores are bad, can be identified from the CP00
1011 * record's PG keyword, only if all of the 8 EQs' value equals 0xE7F9FF. (1EQ
1012 * has 4 cores grouped together by sharing its cache memory.)
1013 * @param [in] pgKeyword - PG Keyword of CPU.
1014 * @return true if the given cpu is an IO, false otherwise.
1015 */
1016static bool isCPUIOGoodOnly(const string& pgKeyword)
1017{
1018 const unsigned char io[] = {0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9,
1019 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7,
1020 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
1021 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
1022 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
1023 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
1024 // IO.
1025 if (memcmp(io, pgKeyword.data() + 97, 24) == 0)
1026 {
1027 return true;
1028 }
1029
1030 // The CPU is not an IO
1031 return false;
1032}
1033
1034/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301035 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301036 * This method invokes all the populateInterface functions
1037 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301038 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
1039 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301040 * @param[in] js - Inventory json object
1041 * @param[in] filePath - Path of the vpd file
1042 * @param[in] preIntrStr - Interface string
1043 */
1044template <typename T>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001045static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301046{
1047 inventory::InterfaceMap interfaces;
1048 inventory::ObjectMap objects;
1049 inventory::PropertyMap prop;
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001050 string ccinFromVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301051
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301052 bool isSystemVpd = (filePath == systemVpdFilePath);
1053 if constexpr (is_same<T, Parsed>::value)
1054 {
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001055 ccinFromVpd = getKwVal(vpdMap, "VINI", "CC");
1056 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1057 ::toupper);
1058
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301059 if (isSystemVpd)
1060 {
1061 std::vector<std::string> interfaces = {motherBoardInterface};
1062 // call mapper to check for object path creation
1063 MapperResponse subTree =
1064 getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
1065 string mboardPath =
1066 js["frus"][filePath].at(0).value("inventoryPath", "");
1067
1068 // Attempt system VPD restore if we have a motherboard
1069 // object in the inventory.
1070 if ((subTree.size() != 0) &&
1071 (subTree.find(pimPath + mboardPath) != subTree.end()))
1072 {
Sunny Srivastava3c244142022-01-11 08:47:04 -06001073 restoreSystemVPD(vpdMap, mboardPath);
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301074 }
1075 else
1076 {
1077 log<level::ERR>("No object path found");
1078 }
1079 }
alpana077ce68722021-07-25 13:23:59 -05001080 else
1081 {
1082 // check if it is processor vpd.
1083 auto isPrimaryCpu = isThisPrimaryProcessor(js, filePath);
1084
1085 if (isPrimaryCpu)
1086 {
1087 auto ddVersion = getKwVal(vpdMap, "CRP0", "DD");
1088
1089 auto chipVersion = atoi(ddVersion.substr(1, 2).c_str());
1090
1091 if (chipVersion >= 2)
1092 {
1093 doEnableAllDimms(js);
Santosh Puranik253fbe92022-10-06 22:38:09 +05301094 // Sleep for a few seconds to let the DIMM parses start
1095 using namespace std::chrono_literals;
1096 std::this_thread::sleep_for(5s);
alpana077ce68722021-07-25 13:23:59 -05001097 }
1098 }
1099 }
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301100 }
1101
Santosh Puranikf3e69682022-03-31 17:52:38 +05301102 auto processFactoryReset = false;
1103
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001104 if (isSystemVpd)
1105 {
1106 string systemJsonName{};
1107 if constexpr (is_same<T, Parsed>::value)
1108 {
1109 // pick the right system json
1110 systemJsonName = getSystemsJson(vpdMap);
1111 }
1112
1113 fs::path target = systemJsonName;
1114 fs::path link = INVENTORY_JSON_SYM_LINK;
1115
Santosh Puranikf3e69682022-03-31 17:52:38 +05301116 // If the symlink does not exist, we treat that as a factory reset
1117 processFactoryReset = !fs::exists(INVENTORY_JSON_SYM_LINK);
1118
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001119 // Create the directory for hosting the symlink
1120 fs::create_directories(VPD_FILES_PATH);
1121 // unlink the symlink previously created (if any)
1122 remove(INVENTORY_JSON_SYM_LINK);
1123 // create a new symlink based on the system
1124 fs::create_symlink(target, link);
1125
1126 // Reloading the json
1127 ifstream inventoryJson(link);
1128 js = json::parse(inventoryJson);
1129 inventoryJson.close();
1130 }
1131
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301132 for (const auto& item : js["frus"][filePath])
1133 {
1134 const auto& objectPath = item["inventoryPath"];
1135 sdbusplus::message::object_path object(objectPath);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001136
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001137 vector<string> ccinList;
1138 if (item.find("ccin") != item.end())
1139 {
1140 for (const auto& cc : item["ccin"])
1141 {
1142 string ccin = cc;
1143 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1144 ccinList.push_back(ccin);
1145 }
1146 }
1147
1148 if (!ccinFromVpd.empty() && !ccinList.empty() &&
1149 (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1150 ccinList.end()))
1151 {
1152 continue;
1153 }
1154
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001155 if ((isSystemVpd) || (item.value("noprime", false)))
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301156 {
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001157
1158 // Populate one time properties for the system VPD and its sub-frus
1159 // and for other non-primeable frus.
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301160 // For the remaining FRUs, this will get handled as a part of
1161 // priming the inventory.
1162 setOneTimeProperties(objectPath, interfaces);
1163 }
1164
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301165 // Populate the VPD keywords and the common interfaces only if we
1166 // are asked to inherit that data from the VPD, else only add the
1167 // extraInterfaces.
1168 if (item.value("inherit", true))
1169 {
Alpana Kumari58e22142020-05-05 00:22:12 -05001170 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301171 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301172 // Each record in the VPD becomes an interface and all
1173 // keyword within the record are properties under that
1174 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301175 for (const auto& record : vpdMap)
1176 {
1177 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001178 record.second, ipzVpdInf + record.first, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301179 }
1180 }
Alpana Kumari58e22142020-05-05 00:22:12 -05001181 else if constexpr (is_same<T, KeywordVpdMap>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301182 {
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001183 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301184 }
Santosh Puranik88edeb62020-03-02 12:00:09 +05301185 if (js.find("commonInterfaces") != js.end())
1186 {
1187 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
1188 isSystemVpd);
1189 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301190 }
Santosh Puranik0859eb62020-03-16 02:56:29 -05001191 else
1192 {
1193 // Check if we have been asked to inherit specific record(s)
Alpana Kumari58e22142020-05-05 00:22:12 -05001194 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik0859eb62020-03-16 02:56:29 -05001195 {
1196 if (item.find("copyRecords") != item.end())
1197 {
1198 for (const auto& record : item["copyRecords"])
1199 {
1200 const string& recordName = record;
1201 if (vpdMap.find(recordName) != vpdMap.end())
1202 {
1203 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001204 vpdMap.at(recordName), ipzVpdInf + recordName,
Santosh Puranik0859eb62020-03-16 02:56:29 -05001205 interfaces);
1206 }
1207 }
1208 }
1209 }
1210 }
Santosh Puranik32c46502022-02-10 08:55:07 +05301211 // Populate interfaces and properties that are common to every FRU
1212 // and additional interface that might be defined on a per-FRU
1213 // basis.
1214 if (item.find("extraInterfaces") != item.end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301215 {
Santosh Puranik32c46502022-02-10 08:55:07 +05301216 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
1217 isSystemVpd);
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001218 if constexpr (is_same<T, Parsed>::value)
1219 {
1220 if (item["extraInterfaces"].find(
1221 "xyz.openbmc_project.Inventory.Item.Cpu") !=
1222 item["extraInterfaces"].end())
1223 {
1224 if (isCPUIOGoodOnly(getKwVal(vpdMap, "CP00", "PG")))
1225 {
Priyanga Ramasamy2c607a92022-04-08 00:30:17 -05001226 interfaces[invItemIntf]["PrettyName"] = "IO Module";
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001227 }
1228 }
1229 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301230 }
Priyanga Ramasamye358acb2022-03-21 14:21:50 -05001231
1232 // embedded property(true or false) says whether the subfru is embedded
1233 // into the parent fru (or) not. VPD sets Present property only for
1234 // embedded frus. If the subfru is not an embedded FRU, the subfru may
1235 // or may not be physically present. Those non embedded frus will always
1236 // have Present=false irrespective of its physical presence or absence.
1237 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
1238 // Present to true for such sub frus.
1239 // Eg: ethernet port is embedded into bmc card. So set Present to true
1240 // for such sub frus. Also donot populate present property for embedded
1241 // subfru which is synthesized. Currently there is no subfru which are
1242 // both embedded and synthesized. But still the case is handled here.
1243 if ((item.value("embedded", true)) &&
1244 (!item.value("synthesized", false)))
1245 {
1246 inventory::PropertyMap presProp;
1247 presProp.emplace("Present", true);
1248 insertOrMerge(interfaces, invItemIntf, move(presProp));
1249 }
Priyanga Ramasamyaa8a8932022-01-27 09:12:41 -06001250
Santosh Puranikf3e69682022-03-31 17:52:38 +05301251 if constexpr (is_same<T, Parsed>::value)
1252 {
1253 // Restore asset tag, if needed
1254 if (processFactoryReset && objectPath == "/system")
1255 {
1256 fillAssetTag(interfaces, vpdMap);
1257 }
1258 }
1259
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301260 objects.emplace(move(object), move(interfaces));
1261 }
1262
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301263 if (isSystemVpd)
1264 {
1265 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
1266 objects.insert(primeObject.begin(), primeObject.end());
Alpana Kumari65b83602020-09-01 00:24:56 -05001267
Alpana Kumarif05effd2021-04-07 07:32:53 -05001268 // set the U-boot environment variable for device-tree
1269 if constexpr (is_same<T, Parsed>::value)
1270 {
Santosh Puranike5f177a2022-01-24 20:14:46 +05301271 setDevTreeEnv(fs::path(getSystemsJson(vpdMap)).filename());
Alpana Kumarif05effd2021-04-07 07:32:53 -05001272 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301273 }
1274
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301275 // Notify PIM
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05001276 common::utility::callPIM(move(objects));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301277}
1278
1279int main(int argc, char** argv)
1280{
1281 int rc = 0;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001282 json js{};
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001283 Binary vpdVector{};
1284 string file{};
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001285 // map to hold additional data in case of logging pel
1286 PelAdditionalData additionalData{};
1287
1288 // this is needed to hold base fru inventory path in case there is ECC or
1289 // vpd exception while parsing the file
1290 std::string baseFruInventoryPath = {};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301291
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001292 // severity for PEL
1293 PelSeverity pelSeverity = PelSeverity::WARNING;
1294
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301295 try
1296 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301297 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
1298 "in DBUS"};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301299
1300 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
Alpana Kumari2f793042020-08-18 05:51:03 -05001301 ->required();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301302
1303 CLI11_PARSE(app, argc, argv);
1304
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001305 // PEL severity should be ERROR in case of any system VPD failure
1306 if (file == systemVpdFilePath)
1307 {
1308 pelSeverity = PelSeverity::ERROR;
1309 }
1310
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301311 auto jsonToParse = INVENTORY_JSON_DEFAULT;
1312
1313 // If the symlink exists, it means it has been setup for us, switch the
1314 // path
1315 if (fs::exists(INVENTORY_JSON_SYM_LINK))
1316 {
1317 jsonToParse = INVENTORY_JSON_SYM_LINK;
1318 }
1319
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301320 // Make sure that the file path we get is for a supported EEPROM
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301321 ifstream inventoryJson(jsonToParse);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001322 if (!inventoryJson)
1323 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001324 throw(VpdJsonException("Failed to access Json path", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001325 }
1326
1327 try
1328 {
1329 js = json::parse(inventoryJson);
1330 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001331 catch (const json::parse_error& ex)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001332 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001333 throw(VpdJsonException("Json parsing failed", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001334 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301335
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301336 // Do we have the mandatory "frus" section?
1337 if (js.find("frus") == js.end())
1338 {
1339 throw(VpdJsonException("FRUs section not found in JSON",
1340 jsonToParse));
1341 }
1342
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301343 // Check if it's a udev path - patterned as(/ahb/ahb:apb/ahb:apb:bus@)
1344 if (file.find("/ahb:apb") != string::npos)
1345 {
1346 // Translate udev path to a generic /sys/bus/.. file path.
1347 udevToGenericPath(file);
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301348
1349 if ((js["frus"].find(file) != js["frus"].end()) &&
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301350 (file == systemVpdFilePath))
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301351 {
Santosh Puranik6b2b5372022-06-02 20:49:02 +05301352 // We have already collected system VPD, skip.
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301353 return 0;
1354 }
1355 }
1356
1357 if (file.empty())
1358 {
1359 cerr << "The EEPROM path <" << file << "> is not valid.";
1360 return 0;
1361 }
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301362 if (js["frus"].find(file) == js["frus"].end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301363 {
Santosh Puranik88edeb62020-03-02 12:00:09 +05301364 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301365 }
1366
Alpana Kumari2f793042020-08-18 05:51:03 -05001367 if (!fs::exists(file))
1368 {
1369 cout << "Device path: " << file
1370 << " does not exist. Spurious udev event? Exiting." << endl;
1371 return 0;
1372 }
1373
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001374 baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
Santosh Puranik85893752020-11-10 21:31:43 +05301375 // Check if we can read the VPD file based on the power state
Santosh Puranik27a5e952021-10-07 22:08:01 -05001376 // We skip reading VPD when the power is ON in two scenarios:
Santosh Puranik31d50fa2022-04-04 12:04:37 +05301377 // 1) The eeprom we are trying to read is that of the system VPD and the
1378 // JSON symlink is already setup (the symlink's existence tells us we
1379 // are not coming out of a factory reset)
1380 // 2) The JSON tells us that the FRU EEPROM cannot be
1381 // read when we are powered ON.
Santosh Puranik27a5e952021-10-07 22:08:01 -05001382 if (js["frus"][file].at(0).value("powerOffOnly", false) ||
Santosh Puranik31d50fa2022-04-04 12:04:37 +05301383 (file == systemVpdFilePath && fs::exists(INVENTORY_JSON_SYM_LINK)))
Santosh Puranik85893752020-11-10 21:31:43 +05301384 {
1385 if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
1386 getPowerState())
1387 {
1388 cout << "This VPD cannot be read when power is ON" << endl;
1389 return 0;
1390 }
1391 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001392
Santosh Puranike9c57532022-03-15 16:51:51 +05301393 // Check if this VPD should be recollected at all
1394 if (!needsRecollection(js, file))
1395 {
1396 cout << "Skip VPD recollection for: " << file << endl;
1397 return 0;
1398 }
1399
Alpana Kumari2f793042020-08-18 05:51:03 -05001400 try
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301401 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001402 vpdVector = getVpdDataInVector(js, file);
PriyangaRamasamy33c61c22021-04-06 11:15:57 -05001403 ParserInterface* parser = ParserFactory::getParser(vpdVector);
Alpana Kumari2f793042020-08-18 05:51:03 -05001404 variant<KeywordVpdMap, Store> parseResult;
1405 parseResult = parser->parse();
SunnySrivastava19849a195542020-09-07 06:04:50 -05001406
Alpana Kumari2f793042020-08-18 05:51:03 -05001407 if (auto pVal = get_if<Store>(&parseResult))
1408 {
1409 populateDbus(pVal->getVpdMap(), js, file);
1410 }
1411 else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
1412 {
1413 populateDbus(*pVal, js, file);
1414 }
1415
1416 // release the parser object
1417 ParserFactory::freeParser(parser);
1418 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001419 catch (const exception& e)
Alpana Kumari2f793042020-08-18 05:51:03 -05001420 {
Alpana Kumari735dee92022-03-25 01:24:40 -05001421 executePostFailAction(js, file);
PriyangaRamasamya504c3e2020-12-06 12:14:52 -06001422 throw;
Alpana Kumari2f793042020-08-18 05:51:03 -05001423 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301424 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001425 catch (const VpdJsonException& ex)
1426 {
1427 additionalData.emplace("JSON_PATH", ex.getJsonPath());
1428 additionalData.emplace("DESCRIPTION", ex.what());
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001429 createPEL(additionalData, pelSeverity, errIntfForJsonFailure);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001430
1431 cerr << ex.what() << "\n";
1432 rc = -1;
1433 }
1434 catch (const VpdEccException& ex)
1435 {
1436 additionalData.emplace("DESCRIPTION", "ECC check failed");
1437 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1438 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001439 createPEL(additionalData, pelSeverity, errIntfForEccCheckFail);
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001440 dumpBadVpd(file, vpdVector);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001441 cerr << ex.what() << "\n";
1442 rc = -1;
1443 }
1444 catch (const VpdDataException& ex)
1445 {
alpana075cb3b1f2021-12-16 11:19:36 -06001446 if (isThisPcieOnPass1planar(js, file))
1447 {
1448 cout << "Pcie_device [" << file
1449 << "]'s VPD is not valid on PASS1 planar.Ignoring.\n";
1450 rc = 0;
1451 }
Santosh Puranik53b38ed2022-04-10 23:15:22 +05301452 else if (!(isPresent(js, file).value_or(true)))
1453 {
1454 cout << "FRU at: " << file
1455 << " is not detected present. Ignore parser error.\n";
1456 rc = 0;
1457 }
alpana075cb3b1f2021-12-16 11:19:36 -06001458 else
1459 {
1460 string errorMsg =
1461 "VPD file is either empty or invalid. Parser failed for [";
1462 errorMsg += file;
1463 errorMsg += "], with error = " + std::string(ex.what());
1464
1465 additionalData.emplace("DESCRIPTION", errorMsg);
1466 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1467 INVENTORY_PATH + baseFruInventoryPath);
1468 createPEL(additionalData, pelSeverity, errIntfForInvalidVPD);
1469
1470 rc = -1;
1471 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001472 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001473 catch (const exception& e)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301474 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001475 dumpBadVpd(file, vpdVector);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301476 cerr << e.what() << "\n";
1477 rc = -1;
1478 }
1479
1480 return rc;
Alpana Kumari735dee92022-03-25 01:24:40 -05001481}