blob: 9dfcdcfa46783540d2e00cca4a9f3ca64cbd4e7c [file] [log] [blame]
SunnySrivastava1984b59fd092020-02-03 09:58:56 -06001#include "config.h"
2
3#include "manager.hpp"
4
Santosh Puranikf2d3b532022-04-19 06:44:07 -05005#include "bios_handler.hpp"
Santosh Puranikbf78ed82022-04-20 13:17:04 +05306#include "common_utility.hpp"
SunnySrivastava1984d076da82020-03-05 05:33:35 -06007#include "editor_impl.hpp"
Alpana Kumarib17dd3b2020-10-01 00:18:10 -05008#include "gpioMonitor.hpp"
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05009#include "ibm_vpd_utils.hpp"
SunnySrivastava1984e12b1812020-05-26 02:23:11 -050010#include "ipz_parser.hpp"
Santosh Puranik6b2b5372022-06-02 20:49:02 +053011#include "parser_factory.hpp"
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -050012#include "reader_impl.hpp"
SunnySrivastava19849a195542020-09-07 06:04:50 -050013#include "vpd_exceptions.hpp"
14
Santosh Puranikd40e42d2022-03-23 13:58:06 +053015#include <filesystem>
SunnySrivastava19849a195542020-09-07 06:04:50 -050016#include <phosphor-logging/elog-errors.hpp>
SunnySrivastava1984b59fd092020-02-03 09:58:56 -060017
Alpana Kumarib17dd3b2020-10-01 00:18:10 -050018using namespace openpower::vpd::manager;
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -060019using namespace openpower::vpd::constants;
SunnySrivastava19841356d7e2020-04-24 04:29:35 -050020using namespace openpower::vpd::inventory;
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -060021using namespace openpower::vpd::manager::editor;
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -050022using namespace openpower::vpd::manager::reader;
SunnySrivastava19849a195542020-09-07 06:04:50 -050023using namespace std;
24using namespace openpower::vpd::parser;
Santosh Puranik6b2b5372022-06-02 20:49:02 +053025using namespace openpower::vpd::parser::factory;
26using namespace openpower::vpd::ipz::parser;
SunnySrivastava19849a195542020-09-07 06:04:50 -050027using namespace openpower::vpd::exceptions;
28using namespace phosphor::logging;
SunnySrivastava1984b59fd092020-02-03 09:58:56 -060029
30namespace openpower
31{
32namespace vpd
33{
34namespace manager
35{
Patrick Williams2eb01762022-07-22 19:26:56 -050036Manager::Manager(sdbusplus::bus_t&& bus, const char* busName,
Priyanga Ramasamy9d149342020-07-16 23:41:26 +053037 const char* objPath, const char* /*iFace*/) :
SunnySrivastava1984b59fd092020-02-03 09:58:56 -060038 ServerObject<ManagerIface>(bus, objPath),
39 _bus(std::move(bus)), _manager(_bus, objPath)
40{
41 _bus.request_name(busName);
42}
43
44void Manager::run()
45{
SunnySrivastava1984de3c60d2020-02-03 10:34:33 -060046 try
47 {
48 processJSON();
Santosh Puranik6b2b5372022-06-02 20:49:02 +053049 restoreSystemVpd();
Sunny Srivastavaecb5c7d2021-09-02 07:20:24 -050050 listenHostState();
Santosh Puranikb62f6052022-04-06 18:37:54 +053051 listenAssetTag();
Alpana Kumarib17dd3b2020-10-01 00:18:10 -050052
Santosh Puranikf2d3b532022-04-19 06:44:07 -050053 // Create an instance of the BIOS handler
54 BiosHandler biosHandler{_bus, *this};
55
Alpana Kumarib17dd3b2020-10-01 00:18:10 -050056 auto event = sdeventplus::Event::get_default();
57 GpioMonitor gpioMon1(jsonFile, event);
58
59 _bus.attach_event(event.get(), SD_EVENT_PRIORITY_IMPORTANT);
60 cout << "VPD manager event loop started\n";
61 event.loop();
SunnySrivastava1984de3c60d2020-02-03 10:34:33 -060062 }
63 catch (const std::exception& e)
64 {
65 std::cerr << e.what() << "\n";
66 }
SunnySrivastava1984de3c60d2020-02-03 10:34:33 -060067}
68
Santosh Puranik6b2b5372022-06-02 20:49:02 +053069/**
70 * @brief An api to get list of blank system VPD properties.
71 * @param[in] vpdMap - IPZ vpd map.
72 * @param[in] objectPath - Object path for the FRU.
73 * @param[out] blankPropertyList - Properties which are blank in System VPD and
74 * needs to be updated as standby.
75 */
76static void
77 getListOfBlankSystemVpd(Parsed& vpdMap, const string& objectPath,
78 std::vector<RestoredEeproms>& blankPropertyList)
79{
80 for (const auto& systemRecKwdPair : svpdKwdMap)
81 {
82 auto it = vpdMap.find(systemRecKwdPair.first);
83
84 // check if record is found in map we got by parser
85 if (it != vpdMap.end())
86 {
87 const auto& kwdListForRecord = systemRecKwdPair.second;
88 for (const auto& keyword : kwdListForRecord)
89 {
90 DbusPropertyMap& kwdValMap = it->second;
91 auto iterator = kwdValMap.find(keyword);
92
93 if (iterator != kwdValMap.end())
94 {
95 string& kwdValue = iterator->second;
96
97 // check bus data
98 const string& recordName = systemRecKwdPair.first;
99 const string& busValue = readBusProperty(
100 objectPath, ipzVpdInf + recordName, keyword);
101
102 if (busValue.find_first_not_of(' ') != string::npos)
103 {
104 if (kwdValue.find_first_not_of(' ') == string::npos)
105 {
106 // implies data is blank on EEPROM but not on cache.
107 // So EEPROM vpd update is required.
108 Binary busData(busValue.begin(), busValue.end());
109
110 blankPropertyList.push_back(std::make_tuple(
111 objectPath, recordName, keyword, busData));
112 }
113 }
114 }
115 }
116 }
117 }
118}
119
120void Manager::restoreSystemVpd()
121{
122 std::cout << "Attempting system VPD restore" << std::endl;
123 ParserInterface* parser = nullptr;
124 try
125 {
126 auto vpdVector = getVpdDataInVector(jsonFile, systemVpdFilePath);
127 parser = ParserFactory::getParser(vpdVector);
128 auto parseResult = parser->parse();
129
130 if (auto pVal = std::get_if<Store>(&parseResult))
131 {
132 // map to hold all the keywords whose value is blank and
133 // needs to be updated at standby.
134 std::vector<RestoredEeproms> blankSystemVpdProperties{};
135 getListOfBlankSystemVpd(pVal->getVpdMap(), SYSTEM_OBJECT,
136 blankSystemVpdProperties);
137
138 // if system VPD restore is required, update the
139 // EEPROM
140 for (const auto& item : blankSystemVpdProperties)
141 {
142 std::cout << "Restoring keyword: " << std::get<2>(item)
143 << std::endl;
144 writeKeyword(std::get<0>(item), std::get<1>(item),
145 std::get<2>(item), std::get<3>(item));
146 }
147 }
148 else
149 {
150 std::cerr << "Not a valid format to restore system VPD"
151 << std::endl;
152 }
153 }
154 catch (const std::exception& e)
155 {
156 std::cerr << "Failed to restore system VPD due to exception: "
157 << e.what() << std::endl;
158 }
159 // release the parser object
160 ParserFactory::freeParser(parser);
161}
162
Sunny Srivastavaecb5c7d2021-09-02 07:20:24 -0500163void Manager::listenHostState()
164{
Patrick Williams2eb01762022-07-22 19:26:56 -0500165 static std::shared_ptr<sdbusplus::bus::match_t> hostState =
166 std::make_shared<sdbusplus::bus::match_t>(
Sunny Srivastavaecb5c7d2021-09-02 07:20:24 -0500167 _bus,
168 sdbusplus::bus::match::rules::propertiesChanged(
169 "/xyz/openbmc_project/state/host0",
170 "xyz.openbmc_project.State.Host"),
Patrick Williams2eb01762022-07-22 19:26:56 -0500171 [this](sdbusplus::message_t& msg) { hostStateCallBack(msg); });
Sunny Srivastavaecb5c7d2021-09-02 07:20:24 -0500172}
173
Patrick Williams2eb01762022-07-22 19:26:56 -0500174void Manager::hostStateCallBack(sdbusplus::message_t& msg)
Sunny Srivastavaecb5c7d2021-09-02 07:20:24 -0500175{
176 if (msg.is_method_error())
177 {
178 std::cerr << "Error in reading signal " << std::endl;
179 }
180
181 Path object;
182 PropertyMap propMap;
183 msg.read(object, propMap);
184 const auto itr = propMap.find("CurrentHostState");
185 if (itr != propMap.end())
186 {
187 if (auto hostState = std::get_if<std::string>(&(itr->second)))
188 {
189 // implies system is moving from standby to power on state
190 if (*hostState == "xyz.openbmc_project.State.Host.HostState."
191 "TransitioningToRunning")
192 {
193 // check and perfrom recollection for FRUs replaceable at
194 // standby.
195 performVPDRecollection();
196 return;
197 }
198 }
199 std::cerr << "Failed to read Host state" << std::endl;
200 }
201}
202
Santosh Puranikb62f6052022-04-06 18:37:54 +0530203void Manager::listenAssetTag()
204{
Patrick Williams2eb01762022-07-22 19:26:56 -0500205 static std::shared_ptr<sdbusplus::bus::match_t> assetMatcher =
206 std::make_shared<sdbusplus::bus::match_t>(
Santosh Puranikb62f6052022-04-06 18:37:54 +0530207 _bus,
208 sdbusplus::bus::match::rules::propertiesChanged(
209 "/xyz/openbmc_project/inventory/system",
210 "xyz.openbmc_project.Inventory.Decorator.AssetTag"),
Patrick Williams2eb01762022-07-22 19:26:56 -0500211 [this](sdbusplus::message_t& msg) { assetTagCallback(msg); });
Santosh Puranikb62f6052022-04-06 18:37:54 +0530212}
213
Patrick Williams2eb01762022-07-22 19:26:56 -0500214void Manager::assetTagCallback(sdbusplus::message_t& msg)
Santosh Puranikb62f6052022-04-06 18:37:54 +0530215{
216 if (msg.is_method_error())
217 {
218 std::cerr << "Error in reading signal " << std::endl;
219 }
220
221 Path object;
222 PropertyMap propMap;
223 msg.read(object, propMap);
224 const auto itr = propMap.find("AssetTag");
225 if (itr != propMap.end())
226 {
227 if (auto assetTag = std::get_if<std::string>(&(itr->second)))
228 {
229 // Call Notify to persist the AssetTag
230 inventory::ObjectMap objectMap = {
231 {std::string{"/system"},
232 {{"xyz.openbmc_project.Inventory.Decorator.AssetTag",
233 {{"AssetTag", *assetTag}}}}}};
234
235 common::utility::callPIM(std::move(objectMap));
236 }
237 else
238 {
239 std::cerr << "Failed to read asset tag" << std::endl;
240 }
241 }
242}
243
SunnySrivastava1984de3c60d2020-02-03 10:34:33 -0600244void Manager::processJSON()
245{
Santosh Puranik0246a4d2020-11-04 16:57:39 +0530246 std::ifstream json(INVENTORY_JSON_SYM_LINK, std::ios::binary);
SunnySrivastava1984de3c60d2020-02-03 10:34:33 -0600247
248 if (!json)
249 {
250 throw std::runtime_error("json file not found");
251 }
252
253 jsonFile = nlohmann::json::parse(json);
254 if (jsonFile.find("frus") == jsonFile.end())
255 {
256 throw std::runtime_error("frus group not found in json");
257 }
258
259 const nlohmann::json& groupFRUS =
260 jsonFile["frus"].get_ref<const nlohmann::json::object_t&>();
261 for (const auto& itemFRUS : groupFRUS.items())
262 {
263 const std::vector<nlohmann::json>& groupEEPROM =
264 itemFRUS.value().get_ref<const nlohmann::json::array_t&>();
265 for (const auto& itemEEPROM : groupEEPROM)
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600266 {
SunnySrivastava198443306542020-04-01 02:50:20 -0500267 bool isMotherboard = false;
Santosh Puranika0b23912022-02-10 13:37:09 +0530268 std::string redundantPath;
269
SunnySrivastava198443306542020-04-01 02:50:20 -0500270 if (itemEEPROM["extraInterfaces"].find(
271 "xyz.openbmc_project.Inventory.Item.Board.Motherboard") !=
272 itemEEPROM["extraInterfaces"].end())
273 {
274 isMotherboard = true;
275 }
Santosh Puranika0b23912022-02-10 13:37:09 +0530276 if (itemEEPROM.find("redundantEeprom") != itemEEPROM.end())
277 {
278 redundantPath = itemEEPROM["redundantEeprom"]
279 .get_ref<const nlohmann::json::string_t&>();
280 }
281 frus.emplace(
282 itemEEPROM["inventoryPath"]
283 .get_ref<const nlohmann::json::string_t&>(),
284 std::make_tuple(itemFRUS.key(), redundantPath, isMotherboard));
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -0500285
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000286 if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) !=
Alpana Kumari920408d2020-05-14 00:07:03 -0500287 itemEEPROM["extraInterfaces"].end())
288 {
289 fruLocationCode.emplace(
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000290 itemEEPROM["extraInterfaces"][IBM_LOCATION_CODE_INF]
Alpana Kumari920408d2020-05-14 00:07:03 -0500291 ["LocationCode"]
292 .get_ref<const nlohmann::json::string_t&>(),
293 itemEEPROM["inventoryPath"]
294 .get_ref<const nlohmann::json::string_t&>());
295 }
SunnySrivastava19849a195542020-09-07 06:04:50 -0500296
Sunny Srivastavaecb5c7d2021-09-02 07:20:24 -0500297 if (itemEEPROM.value("replaceableAtStandby", false))
SunnySrivastava19849a195542020-09-07 06:04:50 -0500298 {
299 replaceableFrus.emplace_back(itemFRUS.key());
300 }
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600301 }
302 }
303}
304
305void Manager::writeKeyword(const sdbusplus::message::object_path path,
306 const std::string recordName,
307 const std::string keyword, const Binary value)
308{
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600309 try
310 {
Santosh Puranik8c796812021-12-01 19:17:56 +0530311 std::string objPath{path};
312 // Strip any inventory prefix in path
313 if (objPath.find(INVENTORY_PATH) == 0)
314 {
315 objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1);
316 }
317
318 if (frus.find(objPath) == frus.end())
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600319 {
320 throw std::runtime_error("Inventory path not found");
321 }
322
Santosh Puranika0b23912022-02-10 13:37:09 +0530323 inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second);
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600324
SunnySrivastava19846d8314d2020-05-15 09:34:58 -0500325 // instantiate editor class to update the data
Santosh Puranik8c796812021-12-01 19:17:56 +0530326 EditorImpl edit(vpdFilePath, jsonFile, recordName, keyword, objPath);
Santosh Puranika0b23912022-02-10 13:37:09 +0530327
328 uint32_t offset = 0;
329 // Setup offset, if any
330 for (const auto& item : jsonFile["frus"][vpdFilePath])
331 {
332 if (item.find("offset") != item.end())
333 {
334 offset = item["offset"];
335 break;
336 }
337 }
338
339 edit.updateKeyword(value, offset, true);
340
341 // If we have a redundant EEPROM to update, then update just the EEPROM,
342 // not the cache since that is already done when we updated the primary
343 if (!std::get<1>(frus.find(objPath)->second).empty())
344 {
345 EditorImpl edit(std::get<1>(frus.find(objPath)->second), jsonFile,
346 recordName, keyword);
347 edit.updateKeyword(value, offset, false);
348 }
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600349
SunnySrivastava198443306542020-04-01 02:50:20 -0500350 // if it is a motehrboard FRU need to check for location expansion
Santosh Puranika0b23912022-02-10 13:37:09 +0530351 if (std::get<2>(frus.find(objPath)->second))
SunnySrivastava198443306542020-04-01 02:50:20 -0500352 {
353 if (recordName == "VCEN" && (keyword == "FC" || keyword == "SE"))
354 {
355 edit.expandLocationCode("fcs");
356 }
357 else if (recordName == "VSYS" &&
358 (keyword == "TM" || keyword == "SE"))
359 {
360 edit.expandLocationCode("mts");
361 }
362 }
363
SunnySrivastava19846d8314d2020-05-15 09:34:58 -0500364 return;
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600365 }
366 catch (const std::exception& e)
367 {
368 std::cerr << e.what() << std::endl;
369 }
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600370}
371
SunnySrivastava19841356d7e2020-04-24 04:29:35 -0500372ListOfPaths
SunnySrivastava1984fb5815a2020-04-24 08:03:52 -0500373 Manager::getFRUsByUnexpandedLocationCode(const LocationCode locationCode,
374 const NodeNumber nodeNumber)
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600375{
SunnySrivastava19841356d7e2020-04-24 04:29:35 -0500376 ReaderImpl read;
377 return read.getFrusAtLocation(locationCode, nodeNumber, fruLocationCode);
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600378}
379
SunnySrivastava19841356d7e2020-04-24 04:29:35 -0500380ListOfPaths
SunnySrivastava1984fb5815a2020-04-24 08:03:52 -0500381 Manager::getFRUsByExpandedLocationCode(const LocationCode locationCode)
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600382{
SunnySrivastava1984fb5815a2020-04-24 08:03:52 -0500383 ReaderImpl read;
384 return read.getFRUsByExpandedLocationCode(locationCode, fruLocationCode);
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600385}
386
SunnySrivastava1984fb5815a2020-04-24 08:03:52 -0500387LocationCode Manager::getExpandedLocationCode(const LocationCode locationCode,
388 const NodeNumber nodeNumber)
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600389{
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -0500390 ReaderImpl read;
391 return read.getExpandedLocationCode(locationCode, nodeNumber,
392 fruLocationCode);
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600393}
SunnySrivastava1984de3c60d2020-02-03 10:34:33 -0600394
SunnySrivastava19849a195542020-09-07 06:04:50 -0500395void Manager::performVPDRecollection()
396{
397 // get list of FRUs replaceable at standby
398 for (const auto& item : replaceableFrus)
399 {
400 const vector<nlohmann::json>& groupEEPROM = jsonFile["frus"][item];
401 const nlohmann::json& singleFru = groupEEPROM[0];
402
403 const string& inventoryPath =
404 singleFru["inventoryPath"]
405 .get_ref<const nlohmann::json::string_t&>();
406
Santosh Puranikd40e42d2022-03-23 13:58:06 +0530407 bool prePostActionRequired = false;
408
409 if ((jsonFile["frus"][item].at(0)).find("preAction") !=
410 jsonFile["frus"][item].at(0).end())
411 {
412 if (!executePreAction(jsonFile, item))
413 {
414 // if the FRU has preAction defined then its execution should
415 // pass to ensure bind/unbind of data.
416 // preAction execution failed. should not call bind/unbind.
417 log<level::ERR>(
418 "Pre-Action execution failed for the FRU",
419 entry("ERROR=%s",
420 ("Inventory path: " + inventoryPath).c_str()));
421 continue;
422 }
423 prePostActionRequired = true;
424 }
425
SunnySrivastava19849a195542020-09-07 06:04:50 -0500426 if ((singleFru.find("devAddress") == singleFru.end()) ||
427 (singleFru.find("driverType") == singleFru.end()) ||
428 (singleFru.find("busType") == singleFru.end()))
429 {
430 // The FRUs is marked for replacement but missing mandatory
431 // fields for recollection. Skip to another replaceable fru.
432 log<level::ERR>(
433 "Recollection Failed as mandatory field missing in Json",
434 entry("ERROR=%s",
435 ("Recollection failed for " + inventoryPath).c_str()));
436 continue;
437 }
438
439 string str = "echo ";
440 string deviceAddress = singleFru["devAddress"];
441 const string& driverType = singleFru["driverType"];
442 const string& busType = singleFru["busType"];
443
444 // devTreeStatus flag is present in json as false to mention
445 // that the EEPROM is not mentioned in device tree. If this flag
446 // is absent consider the value to be true, i.e EEPROM is
447 // mentioned in device tree
448 if (!singleFru.value("devTreeStatus", true))
449 {
450 auto pos = deviceAddress.find('-');
451 if (pos != string::npos)
452 {
453 string busNum = deviceAddress.substr(0, pos);
454 deviceAddress =
455 "0x" + deviceAddress.substr(pos + 1, string::npos);
456
457 string deleteDevice = str + deviceAddress + " > /sys/bus/" +
458 busType + "/devices/" + busType + "-" +
459 busNum + "/delete_device";
460 executeCmd(deleteDevice);
461
462 string addDevice = str + driverType + " " + deviceAddress +
463 " > /sys/bus/" + busType + "/devices/" +
464 busType + "-" + busNum + "/new_device";
465 executeCmd(addDevice);
466 }
467 else
468 {
469 log<level::ERR>(
470 "Wrong format of device address in Json",
471 entry(
472 "ERROR=%s",
473 ("Recollection failed for " + inventoryPath).c_str()));
474 continue;
475 }
476 }
477 else
478 {
Alpana Kumarib17dd3b2020-10-01 00:18:10 -0500479 executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
480 driverType, "/unbind"));
481 executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
482 driverType, "/bind"));
SunnySrivastava19849a195542020-09-07 06:04:50 -0500483 }
Santosh Puranikd40e42d2022-03-23 13:58:06 +0530484
485 // this check is added to avoid file system expensive call in case not
486 // required.
487 if (prePostActionRequired)
488 {
489 // Check if device showed up (test for file)
490 if (!filesystem::exists(item))
491 {
492 // If not, then take failure postAction
493 executePostFailAction(jsonFile, item);
494 }
495 }
SunnySrivastava19849a195542020-09-07 06:04:50 -0500496 }
497}
498
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600499} // namespace manager
500} // namespace vpd
Alpana Kumarib17dd3b2020-10-01 00:18:10 -0500501} // namespace openpower