blob: b625568a75cbc8291aa307abf17bf5f1c2f6aaf0 [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
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500487 try
Alpana Kumari2f793042020-08-18 05:51:03 -0500488 {
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500489 if (executePreAction(json, file))
Alpana Kumari2f793042020-08-18 05:51:03 -0500490 {
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500491 if (json["frus"][file].at(0).find("devAddress") !=
492 json["frus"][file].at(0).end())
Alpana Kumari40d1c192022-03-09 21:16:02 -0600493 {
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500494 // Now bind the device
495 string bind = json["frus"][file].at(0).value("devAddress", "");
496 cout << "Binding device " << bind << endl;
497 string bindCmd = string("echo \"") + bind +
498 string("\" > /sys/bus/i2c/drivers/at24/bind");
499 cout << bindCmd << endl;
500 executeCmd(bindCmd);
501
502 // Check if device showed up (test for file)
503 if (!fs::exists(file))
504 {
505 cerr << "EEPROM " << file
506 << " does not exist. Take failure action" << endl;
507 // If not, then take failure postAction
508 executePostFailAction(json, file);
509 }
510 }
511 else
512 {
513 // missing required informations
514 cerr << "VPD inventory JSON missing basic informations of "
515 "preAction "
516 "for this FRU : ["
517 << file << "]. Executing executePostFailAction." << endl;
518
519 // Take failure postAction
Alpana Kumari40d1c192022-03-09 21:16:02 -0600520 executePostFailAction(json, file);
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500521 return;
Alpana Kumari40d1c192022-03-09 21:16:02 -0600522 }
523 }
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500524 }
525 catch (const GpioException& e)
526 {
527 PelAdditionalData additionalData{};
528 additionalData.emplace("DESCRIPTION", e.what());
529 createPEL(additionalData, PelSeverity::WARNING, errIntfForGpioError,
530 nullptr);
Alpana Kumari2f793042020-08-18 05:51:03 -0500531 }
Alpana Kumari2f793042020-08-18 05:51:03 -0500532}
533
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530534/**
Santosh Puranikf3e69682022-03-31 17:52:38 +0530535 * @brief Fills the Decorator.AssetTag property into the interfaces map
536 *
537 * This function should only be called in cases where we did not find a JSON
538 * symlink. A missing symlink in /var/lib will be considered as a factory reset
539 * and this function will be used to default the AssetTag property.
540 *
541 * @param interfaces A possibly pre-populated map of inetrfaces to properties.
542 * @param vpdMap A VPD map of the system VPD data.
543 */
544static void fillAssetTag(inventory::InterfaceMap& interfaces,
545 const Parsed& vpdMap)
546{
547 // Read the system serial number and MTM
548 // Default asset tag is Server-MTM-System Serial
549 inventory::Interface assetIntf{
550 "xyz.openbmc_project.Inventory.Decorator.AssetTag"};
551 inventory::PropertyMap assetTagProps;
552 std::string defaultAssetTag =
553 std::string{"Server-"} + getKwVal(vpdMap, "VSYS", "TM") +
554 std::string{"-"} + getKwVal(vpdMap, "VSYS", "SE");
555 assetTagProps.emplace("AssetTag", defaultAssetTag);
556 insertOrMerge(interfaces, assetIntf, std::move(assetTagProps));
557}
558
559/**
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530560 * @brief Set certain one time properties in the inventory
561 * Use this function to insert the Functional and Enabled properties into the
562 * inventory map. This function first checks if the object in question already
563 * has these properties hosted on D-Bus, if the property is already there, it is
564 * not modified, hence the name "one time". If the property is not already
565 * present, it will be added to the map with a suitable default value (true for
566 * Functional and false for Enabled)
567 *
568 * @param[in] object - The inventory D-Bus obejct without the inventory prefix.
569 * @param[inout] interfaces - Reference to a map of inventory interfaces to
570 * which the properties will be attached.
571 */
572static void setOneTimeProperties(const std::string& object,
573 inventory::InterfaceMap& interfaces)
574{
575 auto bus = sdbusplus::bus::new_default();
576 auto objectPath = INVENTORY_PATH + object;
577 auto prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
578 objectPath.c_str(),
579 "org.freedesktop.DBus.Properties", "Get");
580 prop.append("xyz.openbmc_project.State.Decorator.OperationalStatus");
581 prop.append("Functional");
582 try
583 {
584 auto result = bus.call(prop);
585 }
586 catch (const sdbusplus::exception::SdBusError& e)
587 {
588 // Treat as property unavailable
589 inventory::PropertyMap prop;
590 prop.emplace("Functional", true);
591 interfaces.emplace(
592 "xyz.openbmc_project.State.Decorator.OperationalStatus",
593 move(prop));
594 }
595 prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
596 objectPath.c_str(),
597 "org.freedesktop.DBus.Properties", "Get");
598 prop.append("xyz.openbmc_project.Object.Enable");
599 prop.append("Enabled");
600 try
601 {
602 auto result = bus.call(prop);
603 }
604 catch (const sdbusplus::exception::SdBusError& e)
605 {
606 // Treat as property unavailable
607 inventory::PropertyMap prop;
608 prop.emplace("Enabled", false);
609 interfaces.emplace("xyz.openbmc_project.Object.Enable", move(prop));
610 }
611}
612
613/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530614 * @brief Prime the Inventory
615 * Prime the inventory by populating only the location code,
616 * type interface and the inventory object for the frus
617 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530618 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530619 * @param[in] jsObject - Reference to vpd inventory json object
620 * @param[in] vpdMap - Reference to the parsed vpd map
621 *
622 * @returns Map of items in extraInterface.
623 */
624template <typename T>
625inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
626 const T& vpdMap)
627{
628 inventory::ObjectMap objects;
629
630 for (auto& itemFRUS : jsObject["frus"].items())
631 {
632 for (auto& itemEEPROM : itemFRUS.value())
633 {
Alpana Kumari2e6c6f72020-12-03 00:10:03 -0600634 // Take pre actions if needed
635 if (itemEEPROM.find("preAction") != itemEEPROM.end())
636 {
637 preAction(jsObject, itemFRUS.key());
638 }
639
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530640 inventory::InterfaceMap interfaces;
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530641 inventory::Object object(itemEEPROM.at("inventoryPath"));
642
Santosh Puranik50f60bf2021-05-26 17:55:06 +0530643 if ((itemFRUS.key() != systemVpdFilePath) &&
644 !itemEEPROM.value("noprime", false))
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530645 {
Alpana Kumaricfd7a752021-02-07 23:23:01 -0600646 inventory::PropertyMap presProp;
Priyanga Ramasamye358acb2022-03-21 14:21:50 -0500647
648 // Do not populate Present property for frus whose
649 // synthesized=true. synthesized=true says the fru is owned by
650 // some other component and not by vpd.
651 if (!itemEEPROM.value("synthesized", false))
652 {
653 presProp.emplace("Present", false);
654 interfaces.emplace("xyz.openbmc_project.Inventory.Item",
655 presProp);
656 }
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530657 setOneTimeProperties(object, interfaces);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530658 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
659 {
660 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
661 {
662 inventory::PropertyMap props;
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000663 if (eI.key() == IBM_LOCATION_CODE_INF)
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530664 {
665 if constexpr (std::is_same<T, Parsed>::value)
666 {
667 for (auto& lC : eI.value().items())
668 {
669 auto propVal = expandLocationCode(
670 lC.value().get<string>(), vpdMap, true);
671
672 props.emplace(move(lC.key()),
673 move(propVal));
Santosh Puranikb0f37492021-06-21 09:42:47 +0530674 interfaces.emplace(XYZ_LOCATION_CODE_INF,
675 props);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530676 interfaces.emplace(move(eI.key()),
677 move(props));
678 }
679 }
680 }
681 else if (eI.key().find("Inventory.Item.") !=
682 string::npos)
683 {
684 interfaces.emplace(move(eI.key()), move(props));
685 }
Santosh Puranikd3a379a2021-08-23 19:12:59 +0530686 else if (eI.key() ==
687 "xyz.openbmc_project.Inventory.Item")
688 {
689 for (auto& val : eI.value().items())
690 {
691 if (val.key() == "PrettyName")
692 {
693 presProp.emplace(val.key(),
694 val.value().get<string>());
695 }
696 }
697 // Use insert_or_assign here as we may already have
698 // inserted the present property only earlier in
699 // this function under this same interface.
700 interfaces.insert_or_assign(eI.key(),
701 move(presProp));
702 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530703 }
704 }
705 objects.emplace(move(object), move(interfaces));
706 }
707 }
708 }
709 return objects;
710}
711
Alpana Kumari65b83602020-09-01 00:24:56 -0500712/**
713 * @brief This API executes command to set environment variable
714 * And then reboot the system
715 * @param[in] key -env key to set new value
716 * @param[in] value -value to set.
717 */
718void setEnvAndReboot(const string& key, const string& value)
719{
720 // set env and reboot and break.
721 executeCmd("/sbin/fw_setenv", key, value);
Andrew Geissler280197e2020-12-08 20:51:49 -0600722 log<level::INFO>("Rebooting BMC to pick up new device tree");
Alpana Kumari65b83602020-09-01 00:24:56 -0500723 // make dbus call to reboot
724 auto bus = sdbusplus::bus::new_default_system();
725 auto method = bus.new_method_call(
726 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
727 "org.freedesktop.systemd1.Manager", "Reboot");
728 bus.call_noreply(method);
729}
730
731/*
732 * @brief This API checks for env var fitconfig.
733 * If not initialised OR updated as per the current system type,
734 * update this env var and reboot the system.
735 *
736 * @param[in] systemType IM kwd in vpd tells about which system type it is.
737 * */
738void setDevTreeEnv(const string& systemType)
739{
Alpana Kumari37e72702021-11-18 11:18:04 -0600740 // Init with default dtb
741 string newDeviceTree = "conf-aspeed-bmc-ibm-rainier-p1.dtb";
Santosh Puranike5f177a2022-01-24 20:14:46 +0530742 static const deviceTreeMap deviceTreeSystemTypeMap = {
743 {RAINIER_2U, "conf-aspeed-bmc-ibm-rainier-p1.dtb"},
744 {RAINIER_2U_V2, "conf-aspeed-bmc-ibm-rainier.dtb"},
745 {RAINIER_4U, "conf-aspeed-bmc-ibm-rainier-4u-p1.dtb"},
746 {RAINIER_4U_V2, "conf-aspeed-bmc-ibm-rainier-4u.dtb"},
747 {RAINIER_1S4U, "conf-aspeed-bmc-ibm-rainier-1s4u.dtb"},
Alpana Kumari1b026112022-03-02 23:41:38 -0600748 {EVEREST, "conf-aspeed-bmc-ibm-everest.dtb"},
749 {EVEREST_V2, "conf-aspeed-bmc-ibm-everest.dtb"}};
Alpana Kumari65b83602020-09-01 00:24:56 -0500750
751 if (deviceTreeSystemTypeMap.find(systemType) !=
752 deviceTreeSystemTypeMap.end())
753 {
754 newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
755 }
Alpana Kumari37e72702021-11-18 11:18:04 -0600756 else
757 {
758 // System type not supported
Alpana Kumariab1e22c2021-11-24 11:03:38 -0600759 string err = "This System type not found/supported in dtb table " +
760 systemType +
761 ".Please check the HW and IM keywords in the system "
762 "VPD.Breaking...";
763
764 // map to hold additional data in case of logging pel
765 PelAdditionalData additionalData{};
766 additionalData.emplace("DESCRIPTION", err);
767 createPEL(additionalData, PelSeverity::WARNING,
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500768 errIntfForInvalidSystemType, nullptr);
Alpana Kumariab1e22c2021-11-24 11:03:38 -0600769 exit(-1);
Alpana Kumari37e72702021-11-18 11:18:04 -0600770 }
Alpana Kumari65b83602020-09-01 00:24:56 -0500771
772 string readVarValue;
773 bool envVarFound = false;
774
775 vector<string> output = executeCmd("/sbin/fw_printenv");
776 for (const auto& entry : output)
777 {
778 size_t pos = entry.find("=");
779 string key = entry.substr(0, pos);
780 if (key != "fitconfig")
781 {
782 continue;
783 }
784
785 envVarFound = true;
786 if (pos + 1 < entry.size())
787 {
788 readVarValue = entry.substr(pos + 1);
789 if (readVarValue.find(newDeviceTree) != string::npos)
790 {
791 // fitconfig is Updated. No action needed
792 break;
793 }
794 }
795 // set env and reboot and break.
796 setEnvAndReboot(key, newDeviceTree);
797 exit(0);
798 }
799
800 // check If env var Not found
801 if (!envVarFound)
802 {
803 setEnvAndReboot("fitconfig", newDeviceTree);
804 }
805}
806
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530807/**
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500808 * @brief API to check if we need to restore system VPD
809 * This functionality is only applicable for IPZ VPD data.
810 * @param[in] vpdMap - IPZ vpd map
811 * @param[in] objectPath - Object path for the FRU
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500812 */
Sunny Srivastava3c244142022-01-11 08:47:04 -0600813void restoreSystemVPD(Parsed& vpdMap, const string& objectPath)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500814{
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500815 for (const auto& systemRecKwdPair : svpdKwdMap)
816 {
817 auto it = vpdMap.find(systemRecKwdPair.first);
818
819 // check if record is found in map we got by parser
820 if (it != vpdMap.end())
821 {
822 const auto& kwdListForRecord = systemRecKwdPair.second;
823 for (const auto& keyword : kwdListForRecord)
824 {
825 DbusPropertyMap& kwdValMap = it->second;
826 auto iterator = kwdValMap.find(keyword);
827
828 if (iterator != kwdValMap.end())
829 {
830 string& kwdValue = iterator->second;
831
832 // check bus data
833 const string& recordName = systemRecKwdPair.first;
834 const string& busValue = readBusProperty(
835 objectPath, ipzVpdInf + recordName, keyword);
836
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -0500837 std::string defaultValue{' '};
838
839 // Explicit check for D0 is required as this keyword will
840 // never be blank and 0x00 should be treated as no value in
841 // this case.
842 if (recordName == "UTIL" && keyword == "D0")
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500843 {
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -0500844 // default value of kwd D0 is 0x00. This kwd will never
845 // be blank.
846 defaultValue = '\0';
847 }
848
849 if (busValue.find_first_not_of(defaultValue) !=
850 string::npos)
851 {
852 if (kwdValue.find_first_not_of(defaultValue) !=
853 string::npos)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500854 {
855 // both the data are present, check for mismatch
856 if (busValue != kwdValue)
857 {
858 string errMsg = "VPD data mismatch on cache "
859 "and hardware for record: ";
860 errMsg += (*it).first;
861 errMsg += " and keyword: ";
862 errMsg += keyword;
863
864 // data mismatch
865 PelAdditionalData additionalData;
866 additionalData.emplace("CALLOUT_INVENTORY_PATH",
867 objectPath);
868
869 additionalData.emplace("DESCRIPTION", errMsg);
870
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500871 createPEL(additionalData, PelSeverity::WARNING,
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500872 errIntfForInvalidVPD, nullptr);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500873 }
874 }
875 else
876 {
877 // implies hardware data is blank
878 // update the map
879 Binary busData(busValue.begin(), busValue.end());
880
Sunny Srivastava90a63b92021-05-26 06:30:24 -0500881 // update the map as well, so that cache data is not
882 // updated as blank while populating VPD map on Dbus
883 // in populateDBus Api
884 kwdValue = busValue;
885 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500886 }
Sunny Srivastavaa559c2d2022-05-02 11:56:45 -0500887 else if (kwdValue.find_first_not_of(defaultValue) ==
888 string::npos)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500889 {
Priyanga Ramasamyda3b2d22022-10-31 05:42:22 -0500890 if (recordName == "VSYS" && keyword == "FV")
891 {
892 // Continue to the next keyword without logging +PEL
893 // for VSYS FV(stores min version of BMC firmware).
894 // Reason:There is a requirement to support blank FV
895 // so that customer can use the system without
896 // upgrading BMC to the minimum required version.
897 continue;
898 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500899 string errMsg = "VPD is blank on both cache and "
900 "hardware for record: ";
901 errMsg += (*it).first;
902 errMsg += " and keyword: ";
903 errMsg += keyword;
904 errMsg += ". SSR need to update hardware VPD.";
905
906 // both the data are blanks, log PEL
907 PelAdditionalData additionalData;
908 additionalData.emplace("CALLOUT_INVENTORY_PATH",
909 objectPath);
910
911 additionalData.emplace("DESCRIPTION", errMsg);
912
913 // log PEL TODO: Block IPL
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500914 createPEL(additionalData, PelSeverity::ERROR,
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -0500915 errIntfForBlankSystemVPD, nullptr);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500916 continue;
917 }
918 }
919 }
920 }
921 }
SunnySrivastava19849094d4f2020-08-05 09:32:29 -0500922}
923
924/**
alpana077ce68722021-07-25 13:23:59 -0500925 * @brief This checks for is this FRU a processor
926 * And if yes, then checks for is this primary
927 *
928 * @param[in] js- vpd json to get the information about this FRU
929 * @param[in] filePath- FRU vpd
930 *
931 * @return true/false
932 */
933bool isThisPrimaryProcessor(nlohmann::json& js, const string& filePath)
934{
935 bool isProcessor = false;
936 bool isPrimary = false;
937
938 for (const auto& item : js["frus"][filePath])
939 {
940 if (item.find("extraInterfaces") != item.end())
941 {
942 for (const auto& eI : item["extraInterfaces"].items())
943 {
944 if (eI.key().find("Inventory.Item.Cpu") != string::npos)
945 {
946 isProcessor = true;
947 }
948 }
949 }
950
951 if (isProcessor)
952 {
953 string cpuType = item.value("cpuType", "");
954 if (cpuType == "primary")
955 {
956 isPrimary = true;
957 }
958 }
959 }
960
961 return (isProcessor && isPrimary);
962}
963
964/**
965 * @brief This finds DIMM vpd in vpd json and enables them by binding the device
966 * driver
967 * @param[in] js- vpd json to iterate through and take action if it is DIMM
968 */
969void doEnableAllDimms(nlohmann::json& js)
970{
971 // iterate over each fru
972 for (const auto& eachFru : js["frus"].items())
973 {
974 // skip the driver binding if eeprom already exists
975 if (fs::exists(eachFru.key()))
976 {
977 continue;
978 }
979
980 for (const auto& eachInventory : eachFru.value())
981 {
982 if (eachInventory.find("extraInterfaces") != eachInventory.end())
983 {
984 for (const auto& eI : eachInventory["extraInterfaces"].items())
985 {
986 if (eI.key().find("Inventory.Item.Dimm") != string::npos)
987 {
988 string dimmVpd = eachFru.key();
989 // fetch it from
990 // "/sys/bus/i2c/drivers/at24/414-0050/eeprom"
991
992 regex matchPatern("([0-9]+-[0-9]{4})");
993 smatch matchFound;
994 if (regex_search(dimmVpd, matchFound, matchPatern))
995 {
996 vector<string> i2cReg;
997 boost::split(i2cReg, matchFound.str(0),
998 boost::is_any_of("-"));
999
1000 // remove 0s from begining
1001 const regex pattern("^0+(?!$)");
1002 for (auto& i : i2cReg)
1003 {
1004 i = regex_replace(i, pattern, "");
1005 }
1006
1007 if (i2cReg.size() == 2)
1008 {
1009 // echo 24c32 0x50 >
1010 // /sys/bus/i2c/devices/i2c-16/new_device
1011 string cmnd = "echo 24c32 0x" + i2cReg[1] +
1012 " > /sys/bus/i2c/devices/i2c-" +
1013 i2cReg[0] + "/new_device";
1014
1015 executeCmd(cmnd);
1016 }
1017 }
1018 }
1019 }
1020 }
1021 }
1022 }
1023}
1024
1025/**
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001026 * @brief Check if the given CPU is an IO only chip.
1027 * The CPU is termed as IO, whose all of the cores are bad and can never be
1028 * used. Those CPU chips can be used for IO purpose like connecting PCIe devices
1029 * etc., The CPU whose every cores are bad, can be identified from the CP00
1030 * record's PG keyword, only if all of the 8 EQs' value equals 0xE7F9FF. (1EQ
1031 * has 4 cores grouped together by sharing its cache memory.)
1032 * @param [in] pgKeyword - PG Keyword of CPU.
1033 * @return true if the given cpu is an IO, false otherwise.
1034 */
1035static bool isCPUIOGoodOnly(const string& pgKeyword)
1036{
1037 const unsigned char io[] = {0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9,
1038 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7,
1039 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
1040 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
1041 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
1042 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
1043 // IO.
1044 if (memcmp(io, pgKeyword.data() + 97, 24) == 0)
1045 {
1046 return true;
1047 }
1048
1049 // The CPU is not an IO
1050 return false;
1051}
1052
1053/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301054 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301055 * This method invokes all the populateInterface functions
1056 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301057 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
1058 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301059 * @param[in] js - Inventory json object
1060 * @param[in] filePath - Path of the vpd file
1061 * @param[in] preIntrStr - Interface string
1062 */
1063template <typename T>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001064static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301065{
1066 inventory::InterfaceMap interfaces;
1067 inventory::ObjectMap objects;
1068 inventory::PropertyMap prop;
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001069 string ccinFromVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301070
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301071 bool isSystemVpd = (filePath == systemVpdFilePath);
1072 if constexpr (is_same<T, Parsed>::value)
1073 {
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001074 ccinFromVpd = getKwVal(vpdMap, "VINI", "CC");
1075 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1076 ::toupper);
1077
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301078 if (isSystemVpd)
1079 {
1080 std::vector<std::string> interfaces = {motherBoardInterface};
1081 // call mapper to check for object path creation
1082 MapperResponse subTree =
1083 getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
1084 string mboardPath =
1085 js["frus"][filePath].at(0).value("inventoryPath", "");
1086
1087 // Attempt system VPD restore if we have a motherboard
1088 // object in the inventory.
1089 if ((subTree.size() != 0) &&
1090 (subTree.find(pimPath + mboardPath) != subTree.end()))
1091 {
Sunny Srivastava3c244142022-01-11 08:47:04 -06001092 restoreSystemVPD(vpdMap, mboardPath);
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301093 }
1094 else
1095 {
1096 log<level::ERR>("No object path found");
1097 }
1098 }
alpana077ce68722021-07-25 13:23:59 -05001099 else
1100 {
1101 // check if it is processor vpd.
1102 auto isPrimaryCpu = isThisPrimaryProcessor(js, filePath);
1103
1104 if (isPrimaryCpu)
1105 {
1106 auto ddVersion = getKwVal(vpdMap, "CRP0", "DD");
1107
1108 auto chipVersion = atoi(ddVersion.substr(1, 2).c_str());
1109
1110 if (chipVersion >= 2)
1111 {
1112 doEnableAllDimms(js);
Santosh Puranik253fbe92022-10-06 22:38:09 +05301113 // Sleep for a few seconds to let the DIMM parses start
1114 using namespace std::chrono_literals;
1115 std::this_thread::sleep_for(5s);
alpana077ce68722021-07-25 13:23:59 -05001116 }
1117 }
1118 }
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301119 }
1120
Santosh Puranikf3e69682022-03-31 17:52:38 +05301121 auto processFactoryReset = false;
1122
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001123 if (isSystemVpd)
1124 {
1125 string systemJsonName{};
1126 if constexpr (is_same<T, Parsed>::value)
1127 {
1128 // pick the right system json
1129 systemJsonName = getSystemsJson(vpdMap);
1130 }
1131
1132 fs::path target = systemJsonName;
1133 fs::path link = INVENTORY_JSON_SYM_LINK;
1134
Santosh Puranikf3e69682022-03-31 17:52:38 +05301135 // If the symlink does not exist, we treat that as a factory reset
1136 processFactoryReset = !fs::exists(INVENTORY_JSON_SYM_LINK);
1137
Priyanga Ramasamy32c687f2022-01-04 23:14:03 -06001138 // Create the directory for hosting the symlink
1139 fs::create_directories(VPD_FILES_PATH);
1140 // unlink the symlink previously created (if any)
1141 remove(INVENTORY_JSON_SYM_LINK);
1142 // create a new symlink based on the system
1143 fs::create_symlink(target, link);
1144
1145 // Reloading the json
1146 ifstream inventoryJson(link);
1147 js = json::parse(inventoryJson);
1148 inventoryJson.close();
1149 }
1150
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301151 for (const auto& item : js["frus"][filePath])
1152 {
1153 const auto& objectPath = item["inventoryPath"];
1154 sdbusplus::message::object_path object(objectPath);
SunnySrivastava19849094d4f2020-08-05 09:32:29 -05001155
Shantappa Teekappanavar6aa54502021-12-09 12:59:56 -06001156 vector<string> ccinList;
1157 if (item.find("ccin") != item.end())
1158 {
1159 for (const auto& cc : item["ccin"])
1160 {
1161 string ccin = cc;
1162 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1163 ccinList.push_back(ccin);
1164 }
1165 }
1166
1167 if (!ccinFromVpd.empty() && !ccinList.empty() &&
1168 (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1169 ccinList.end()))
1170 {
1171 continue;
1172 }
1173
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001174 if ((isSystemVpd) || (item.value("noprime", false)))
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301175 {
Priyanga Ramasamye3fed702022-01-11 01:05:32 -06001176
1177 // Populate one time properties for the system VPD and its sub-frus
1178 // and for other non-primeable frus.
Santosh Puranikd3a379a2021-08-23 19:12:59 +05301179 // For the remaining FRUs, this will get handled as a part of
1180 // priming the inventory.
1181 setOneTimeProperties(objectPath, interfaces);
1182 }
1183
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301184 // Populate the VPD keywords and the common interfaces only if we
1185 // are asked to inherit that data from the VPD, else only add the
1186 // extraInterfaces.
1187 if (item.value("inherit", true))
1188 {
Alpana Kumari58e22142020-05-05 00:22:12 -05001189 if constexpr (is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301190 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301191 // Each record in the VPD becomes an interface and all
1192 // keyword within the record are properties under that
1193 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301194 for (const auto& record : vpdMap)
1195 {
1196 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001197 record.second, ipzVpdInf + record.first, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301198 }
1199 }
Alpana Kumari58e22142020-05-05 00:22:12 -05001200 else if constexpr (is_same<T, KeywordVpdMap>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301201 {
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001202 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301203 }
Santosh Puranik88edeb62020-03-02 12:00:09 +05301204 if (js.find("commonInterfaces") != js.end())
1205 {
1206 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
1207 isSystemVpd);
1208 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301209 }
Santosh Puranik0859eb62020-03-16 02:56:29 -05001210 else
1211 {
1212 // Check if we have been asked to inherit specific record(s)
Alpana Kumari58e22142020-05-05 00:22:12 -05001213 if constexpr (is_same<T, Parsed>::value)
Santosh Puranik0859eb62020-03-16 02:56:29 -05001214 {
1215 if (item.find("copyRecords") != item.end())
1216 {
1217 for (const auto& record : item["copyRecords"])
1218 {
1219 const string& recordName = record;
1220 if (vpdMap.find(recordName) != vpdMap.end())
1221 {
1222 populateFruSpecificInterfaces(
SunnySrivastava1984e12b1812020-05-26 02:23:11 -05001223 vpdMap.at(recordName), ipzVpdInf + recordName,
Santosh Puranik0859eb62020-03-16 02:56:29 -05001224 interfaces);
1225 }
1226 }
1227 }
1228 }
1229 }
Santosh Puranik32c46502022-02-10 08:55:07 +05301230 // Populate interfaces and properties that are common to every FRU
1231 // and additional interface that might be defined on a per-FRU
1232 // basis.
1233 if (item.find("extraInterfaces") != item.end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301234 {
Santosh Puranik32c46502022-02-10 08:55:07 +05301235 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
1236 isSystemVpd);
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001237 if constexpr (is_same<T, Parsed>::value)
1238 {
1239 if (item["extraInterfaces"].find(
1240 "xyz.openbmc_project.Inventory.Item.Cpu") !=
1241 item["extraInterfaces"].end())
1242 {
1243 if (isCPUIOGoodOnly(getKwVal(vpdMap, "CP00", "PG")))
1244 {
Priyanga Ramasamy2c607a92022-04-08 00:30:17 -05001245 interfaces[invItemIntf]["PrettyName"] = "IO Module";
Priyanga Ramasamy6abdeb62022-01-09 23:15:11 -06001246 }
1247 }
1248 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301249 }
Priyanga Ramasamye358acb2022-03-21 14:21:50 -05001250
1251 // embedded property(true or false) says whether the subfru is embedded
1252 // into the parent fru (or) not. VPD sets Present property only for
1253 // embedded frus. If the subfru is not an embedded FRU, the subfru may
1254 // or may not be physically present. Those non embedded frus will always
1255 // have Present=false irrespective of its physical presence or absence.
1256 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
1257 // Present to true for such sub frus.
1258 // Eg: ethernet port is embedded into bmc card. So set Present to true
1259 // for such sub frus. Also donot populate present property for embedded
1260 // subfru which is synthesized. Currently there is no subfru which are
1261 // both embedded and synthesized. But still the case is handled here.
1262 if ((item.value("embedded", true)) &&
1263 (!item.value("synthesized", false)))
1264 {
1265 inventory::PropertyMap presProp;
1266 presProp.emplace("Present", true);
1267 insertOrMerge(interfaces, invItemIntf, move(presProp));
1268 }
Priyanga Ramasamyaa8a8932022-01-27 09:12:41 -06001269
Santosh Puranikf3e69682022-03-31 17:52:38 +05301270 if constexpr (is_same<T, Parsed>::value)
1271 {
1272 // Restore asset tag, if needed
1273 if (processFactoryReset && objectPath == "/system")
1274 {
1275 fillAssetTag(interfaces, vpdMap);
1276 }
1277 }
1278
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301279 objects.emplace(move(object), move(interfaces));
1280 }
1281
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301282 if (isSystemVpd)
1283 {
1284 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
1285 objects.insert(primeObject.begin(), primeObject.end());
Alpana Kumari65b83602020-09-01 00:24:56 -05001286
Alpana Kumarif05effd2021-04-07 07:32:53 -05001287 // set the U-boot environment variable for device-tree
1288 if constexpr (is_same<T, Parsed>::value)
1289 {
Santosh Puranike5f177a2022-01-24 20:14:46 +05301290 setDevTreeEnv(fs::path(getSystemsJson(vpdMap)).filename());
Alpana Kumarif05effd2021-04-07 07:32:53 -05001291 }
PriyangaRamasamy8e140a12020-04-13 19:24:03 +05301292 }
1293
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301294 // Notify PIM
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05001295 common::utility::callPIM(move(objects));
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301296}
1297
1298int main(int argc, char** argv)
1299{
1300 int rc = 0;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001301 json js{};
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001302 Binary vpdVector{};
1303 string file{};
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001304 // map to hold additional data in case of logging pel
1305 PelAdditionalData additionalData{};
1306
1307 // this is needed to hold base fru inventory path in case there is ECC or
1308 // vpd exception while parsing the file
1309 std::string baseFruInventoryPath = {};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301310
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001311 // severity for PEL
1312 PelSeverity pelSeverity = PelSeverity::WARNING;
1313
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301314 try
1315 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301316 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
1317 "in DBUS"};
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301318
1319 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
Alpana Kumari2f793042020-08-18 05:51:03 -05001320 ->required();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301321
1322 CLI11_PARSE(app, argc, argv);
1323
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001324 // PEL severity should be ERROR in case of any system VPD failure
1325 if (file == systemVpdFilePath)
1326 {
1327 pelSeverity = PelSeverity::ERROR;
1328 }
1329
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301330 auto jsonToParse = INVENTORY_JSON_DEFAULT;
1331
1332 // If the symlink exists, it means it has been setup for us, switch the
1333 // path
1334 if (fs::exists(INVENTORY_JSON_SYM_LINK))
1335 {
1336 jsonToParse = INVENTORY_JSON_SYM_LINK;
1337 }
1338
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301339 // Make sure that the file path we get is for a supported EEPROM
Santosh Puranik0246a4d2020-11-04 16:57:39 +05301340 ifstream inventoryJson(jsonToParse);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001341 if (!inventoryJson)
1342 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001343 throw(VpdJsonException("Failed to access Json path", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001344 }
1345
1346 try
1347 {
1348 js = json::parse(inventoryJson);
1349 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001350 catch (const json::parse_error& ex)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001351 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -05001352 throw(VpdJsonException("Json parsing failed", jsonToParse));
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001353 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301354
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301355 // Do we have the mandatory "frus" section?
1356 if (js.find("frus") == js.end())
1357 {
1358 throw(VpdJsonException("FRUs section not found in JSON",
1359 jsonToParse));
1360 }
1361
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301362 // Check if it's a udev path - patterned as(/ahb/ahb:apb/ahb:apb:bus@)
1363 if (file.find("/ahb:apb") != string::npos)
1364 {
1365 // Translate udev path to a generic /sys/bus/.. file path.
1366 udevToGenericPath(file);
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301367
1368 if ((js["frus"].find(file) != js["frus"].end()) &&
Santosh Puranik50f60bf2021-05-26 17:55:06 +05301369 (file == systemVpdFilePath))
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301370 {
Santosh Puranik6b2b5372022-06-02 20:49:02 +05301371 // We have already collected system VPD, skip.
PriyangaRamasamy647868e2020-09-08 17:03:19 +05301372 return 0;
1373 }
1374 }
1375
1376 if (file.empty())
1377 {
1378 cerr << "The EEPROM path <" << file << "> is not valid.";
1379 return 0;
1380 }
Santosh Puranik12e24ff2021-05-11 19:33:50 +05301381 if (js["frus"].find(file) == js["frus"].end())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301382 {
Santosh Puranik88edeb62020-03-02 12:00:09 +05301383 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301384 }
1385
Alpana Kumari2f793042020-08-18 05:51:03 -05001386 if (!fs::exists(file))
1387 {
1388 cout << "Device path: " << file
1389 << " does not exist. Spurious udev event? Exiting." << endl;
1390 return 0;
1391 }
1392
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001393 baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
Santosh Puranik85893752020-11-10 21:31:43 +05301394 // Check if we can read the VPD file based on the power state
Santosh Puranik27a5e952021-10-07 22:08:01 -05001395 // We skip reading VPD when the power is ON in two scenarios:
Santosh Puranik31d50fa2022-04-04 12:04:37 +05301396 // 1) The eeprom we are trying to read is that of the system VPD and the
1397 // JSON symlink is already setup (the symlink's existence tells us we
1398 // are not coming out of a factory reset)
1399 // 2) The JSON tells us that the FRU EEPROM cannot be
1400 // read when we are powered ON.
Santosh Puranik27a5e952021-10-07 22:08:01 -05001401 if (js["frus"][file].at(0).value("powerOffOnly", false) ||
Santosh Puranik31d50fa2022-04-04 12:04:37 +05301402 (file == systemVpdFilePath && fs::exists(INVENTORY_JSON_SYM_LINK)))
Santosh Puranik85893752020-11-10 21:31:43 +05301403 {
1404 if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
1405 getPowerState())
1406 {
1407 cout << "This VPD cannot be read when power is ON" << endl;
1408 return 0;
1409 }
1410 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001411
Santosh Puranike9c57532022-03-15 16:51:51 +05301412 // Check if this VPD should be recollected at all
1413 if (!needsRecollection(js, file))
1414 {
1415 cout << "Skip VPD recollection for: " << file << endl;
1416 return 0;
1417 }
1418
Alpana Kumari2f793042020-08-18 05:51:03 -05001419 try
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301420 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001421 vpdVector = getVpdDataInVector(js, file);
Sunny Srivastavaf31a91b2022-06-09 08:11:29 -05001422 ParserInterface* parser = ParserFactory::getParser(
1423 vpdVector, (pimPath + baseFruInventoryPath));
Alpana Kumari2f793042020-08-18 05:51:03 -05001424 variant<KeywordVpdMap, Store> parseResult;
1425 parseResult = parser->parse();
SunnySrivastava19849a195542020-09-07 06:04:50 -05001426
Alpana Kumari2f793042020-08-18 05:51:03 -05001427 if (auto pVal = get_if<Store>(&parseResult))
1428 {
1429 populateDbus(pVal->getVpdMap(), js, file);
1430 }
1431 else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
1432 {
1433 populateDbus(*pVal, js, file);
1434 }
1435
1436 // release the parser object
1437 ParserFactory::freeParser(parser);
1438 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001439 catch (const exception& e)
Alpana Kumari2f793042020-08-18 05:51:03 -05001440 {
Alpana Kumari735dee92022-03-25 01:24:40 -05001441 executePostFailAction(js, file);
PriyangaRamasamya504c3e2020-12-06 12:14:52 -06001442 throw;
Alpana Kumari2f793042020-08-18 05:51:03 -05001443 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301444 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001445 catch (const VpdJsonException& ex)
1446 {
1447 additionalData.emplace("JSON_PATH", ex.getJsonPath());
1448 additionalData.emplace("DESCRIPTION", ex.what());
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -05001449 createPEL(additionalData, pelSeverity, errIntfForJsonFailure, nullptr);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001450
1451 cerr << ex.what() << "\n";
1452 rc = -1;
1453 }
1454 catch (const VpdEccException& ex)
1455 {
1456 additionalData.emplace("DESCRIPTION", "ECC check failed");
1457 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1458 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -05001459 createPEL(additionalData, pelSeverity, errIntfForEccCheckFail, nullptr);
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001460 dumpBadVpd(file, vpdVector);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001461 cerr << ex.what() << "\n";
1462 rc = -1;
1463 }
1464 catch (const VpdDataException& ex)
1465 {
alpana075cb3b1f2021-12-16 11:19:36 -06001466 if (isThisPcieOnPass1planar(js, file))
1467 {
1468 cout << "Pcie_device [" << file
1469 << "]'s VPD is not valid on PASS1 planar.Ignoring.\n";
1470 rc = 0;
1471 }
Santosh Puranik53b38ed2022-04-10 23:15:22 +05301472 else if (!(isPresent(js, file).value_or(true)))
1473 {
1474 cout << "FRU at: " << file
1475 << " is not detected present. Ignore parser error.\n";
1476 rc = 0;
1477 }
alpana075cb3b1f2021-12-16 11:19:36 -06001478 else
1479 {
1480 string errorMsg =
1481 "VPD file is either empty or invalid. Parser failed for [";
1482 errorMsg += file;
1483 errorMsg += "], with error = " + std::string(ex.what());
1484
1485 additionalData.emplace("DESCRIPTION", errorMsg);
1486 additionalData.emplace("CALLOUT_INVENTORY_PATH",
1487 INVENTORY_PATH + baseFruInventoryPath);
Sunny Srivastavaa2ddc962022-06-29 08:53:16 -05001488 createPEL(additionalData, pelSeverity, errIntfForInvalidVPD,
1489 nullptr);
alpana075cb3b1f2021-12-16 11:19:36 -06001490
1491 rc = -1;
1492 }
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -05001493 }
Patrick Williams8e15b932021-10-06 13:04:22 -05001494 catch (const exception& e)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301495 {
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06001496 dumpBadVpd(file, vpdVector);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301497 cerr << e.what() << "\n";
1498 rc = -1;
1499 }
1500
1501 return rc;
Alpana Kumari735dee92022-03-25 01:24:40 -05001502}