blob: 36e9753c719308d20bebda55c539c57d0b98f547 [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{
36Manager::Manager(sdbusplus::bus::bus&& 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{
165 static std::shared_ptr<sdbusplus::bus::match::match> hostState =
166 std::make_shared<sdbusplus::bus::match::match>(
167 _bus,
168 sdbusplus::bus::match::rules::propertiesChanged(
169 "/xyz/openbmc_project/state/host0",
170 "xyz.openbmc_project.State.Host"),
171 [this](sdbusplus::message::message& msg) {
172 hostStateCallBack(msg);
173 });
174}
175
176void Manager::hostStateCallBack(sdbusplus::message::message& msg)
177{
178 if (msg.is_method_error())
179 {
180 std::cerr << "Error in reading signal " << std::endl;
181 }
182
183 Path object;
184 PropertyMap propMap;
185 msg.read(object, propMap);
186 const auto itr = propMap.find("CurrentHostState");
187 if (itr != propMap.end())
188 {
189 if (auto hostState = std::get_if<std::string>(&(itr->second)))
190 {
191 // implies system is moving from standby to power on state
192 if (*hostState == "xyz.openbmc_project.State.Host.HostState."
193 "TransitioningToRunning")
194 {
195 // check and perfrom recollection for FRUs replaceable at
196 // standby.
197 performVPDRecollection();
198 return;
199 }
200 }
201 std::cerr << "Failed to read Host state" << std::endl;
202 }
203}
204
Santosh Puranikb62f6052022-04-06 18:37:54 +0530205void Manager::listenAssetTag()
206{
207 static std::shared_ptr<sdbusplus::bus::match::match> assetMatcher =
208 std::make_shared<sdbusplus::bus::match::match>(
209 _bus,
210 sdbusplus::bus::match::rules::propertiesChanged(
211 "/xyz/openbmc_project/inventory/system",
212 "xyz.openbmc_project.Inventory.Decorator.AssetTag"),
213 [this](sdbusplus::message::message& msg) {
214 assetTagCallback(msg);
215 });
216}
217
218void Manager::assetTagCallback(sdbusplus::message::message& msg)
219{
220 if (msg.is_method_error())
221 {
222 std::cerr << "Error in reading signal " << std::endl;
223 }
224
225 Path object;
226 PropertyMap propMap;
227 msg.read(object, propMap);
228 const auto itr = propMap.find("AssetTag");
229 if (itr != propMap.end())
230 {
231 if (auto assetTag = std::get_if<std::string>(&(itr->second)))
232 {
233 // Call Notify to persist the AssetTag
234 inventory::ObjectMap objectMap = {
235 {std::string{"/system"},
236 {{"xyz.openbmc_project.Inventory.Decorator.AssetTag",
237 {{"AssetTag", *assetTag}}}}}};
238
239 common::utility::callPIM(std::move(objectMap));
240 }
241 else
242 {
243 std::cerr << "Failed to read asset tag" << std::endl;
244 }
245 }
246}
247
SunnySrivastava1984de3c60d2020-02-03 10:34:33 -0600248void Manager::processJSON()
249{
Santosh Puranik0246a4d2020-11-04 16:57:39 +0530250 std::ifstream json(INVENTORY_JSON_SYM_LINK, std::ios::binary);
SunnySrivastava1984de3c60d2020-02-03 10:34:33 -0600251
252 if (!json)
253 {
254 throw std::runtime_error("json file not found");
255 }
256
257 jsonFile = nlohmann::json::parse(json);
258 if (jsonFile.find("frus") == jsonFile.end())
259 {
260 throw std::runtime_error("frus group not found in json");
261 }
262
263 const nlohmann::json& groupFRUS =
264 jsonFile["frus"].get_ref<const nlohmann::json::object_t&>();
265 for (const auto& itemFRUS : groupFRUS.items())
266 {
267 const std::vector<nlohmann::json>& groupEEPROM =
268 itemFRUS.value().get_ref<const nlohmann::json::array_t&>();
269 for (const auto& itemEEPROM : groupEEPROM)
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600270 {
SunnySrivastava198443306542020-04-01 02:50:20 -0500271 bool isMotherboard = false;
Santosh Puranika0b23912022-02-10 13:37:09 +0530272 std::string redundantPath;
273
SunnySrivastava198443306542020-04-01 02:50:20 -0500274 if (itemEEPROM["extraInterfaces"].find(
275 "xyz.openbmc_project.Inventory.Item.Board.Motherboard") !=
276 itemEEPROM["extraInterfaces"].end())
277 {
278 isMotherboard = true;
279 }
Santosh Puranika0b23912022-02-10 13:37:09 +0530280 if (itemEEPROM.find("redundantEeprom") != itemEEPROM.end())
281 {
282 redundantPath = itemEEPROM["redundantEeprom"]
283 .get_ref<const nlohmann::json::string_t&>();
284 }
285 frus.emplace(
286 itemEEPROM["inventoryPath"]
287 .get_ref<const nlohmann::json::string_t&>(),
288 std::make_tuple(itemFRUS.key(), redundantPath, isMotherboard));
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -0500289
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000290 if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) !=
Alpana Kumari920408d2020-05-14 00:07:03 -0500291 itemEEPROM["extraInterfaces"].end())
292 {
293 fruLocationCode.emplace(
Alpana Kumari414d5ae2021-03-04 21:06:35 +0000294 itemEEPROM["extraInterfaces"][IBM_LOCATION_CODE_INF]
Alpana Kumari920408d2020-05-14 00:07:03 -0500295 ["LocationCode"]
296 .get_ref<const nlohmann::json::string_t&>(),
297 itemEEPROM["inventoryPath"]
298 .get_ref<const nlohmann::json::string_t&>());
299 }
SunnySrivastava19849a195542020-09-07 06:04:50 -0500300
Sunny Srivastavaecb5c7d2021-09-02 07:20:24 -0500301 if (itemEEPROM.value("replaceableAtStandby", false))
SunnySrivastava19849a195542020-09-07 06:04:50 -0500302 {
303 replaceableFrus.emplace_back(itemFRUS.key());
304 }
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600305 }
306 }
307}
308
309void Manager::writeKeyword(const sdbusplus::message::object_path path,
310 const std::string recordName,
311 const std::string keyword, const Binary value)
312{
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600313 try
314 {
Santosh Puranik8c796812021-12-01 19:17:56 +0530315 std::string objPath{path};
316 // Strip any inventory prefix in path
317 if (objPath.find(INVENTORY_PATH) == 0)
318 {
319 objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1);
320 }
321
322 if (frus.find(objPath) == frus.end())
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600323 {
324 throw std::runtime_error("Inventory path not found");
325 }
326
Santosh Puranika0b23912022-02-10 13:37:09 +0530327 inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second);
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600328
SunnySrivastava19846d8314d2020-05-15 09:34:58 -0500329 // instantiate editor class to update the data
Santosh Puranik8c796812021-12-01 19:17:56 +0530330 EditorImpl edit(vpdFilePath, jsonFile, recordName, keyword, objPath);
Santosh Puranika0b23912022-02-10 13:37:09 +0530331
332 uint32_t offset = 0;
333 // Setup offset, if any
334 for (const auto& item : jsonFile["frus"][vpdFilePath])
335 {
336 if (item.find("offset") != item.end())
337 {
338 offset = item["offset"];
339 break;
340 }
341 }
342
343 edit.updateKeyword(value, offset, true);
344
345 // If we have a redundant EEPROM to update, then update just the EEPROM,
346 // not the cache since that is already done when we updated the primary
347 if (!std::get<1>(frus.find(objPath)->second).empty())
348 {
349 EditorImpl edit(std::get<1>(frus.find(objPath)->second), jsonFile,
350 recordName, keyword);
351 edit.updateKeyword(value, offset, false);
352 }
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600353
SunnySrivastava198443306542020-04-01 02:50:20 -0500354 // if it is a motehrboard FRU need to check for location expansion
Santosh Puranika0b23912022-02-10 13:37:09 +0530355 if (std::get<2>(frus.find(objPath)->second))
SunnySrivastava198443306542020-04-01 02:50:20 -0500356 {
357 if (recordName == "VCEN" && (keyword == "FC" || keyword == "SE"))
358 {
359 edit.expandLocationCode("fcs");
360 }
361 else if (recordName == "VSYS" &&
362 (keyword == "TM" || keyword == "SE"))
363 {
364 edit.expandLocationCode("mts");
365 }
366 }
367
SunnySrivastava19846d8314d2020-05-15 09:34:58 -0500368 return;
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -0600369 }
370 catch (const std::exception& e)
371 {
372 std::cerr << e.what() << std::endl;
373 }
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600374}
375
SunnySrivastava19841356d7e2020-04-24 04:29:35 -0500376ListOfPaths
SunnySrivastava1984fb5815a2020-04-24 08:03:52 -0500377 Manager::getFRUsByUnexpandedLocationCode(const LocationCode locationCode,
378 const NodeNumber nodeNumber)
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600379{
SunnySrivastava19841356d7e2020-04-24 04:29:35 -0500380 ReaderImpl read;
381 return read.getFrusAtLocation(locationCode, nodeNumber, fruLocationCode);
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600382}
383
SunnySrivastava19841356d7e2020-04-24 04:29:35 -0500384ListOfPaths
SunnySrivastava1984fb5815a2020-04-24 08:03:52 -0500385 Manager::getFRUsByExpandedLocationCode(const LocationCode locationCode)
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600386{
SunnySrivastava1984fb5815a2020-04-24 08:03:52 -0500387 ReaderImpl read;
388 return read.getFRUsByExpandedLocationCode(locationCode, fruLocationCode);
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600389}
390
SunnySrivastava1984fb5815a2020-04-24 08:03:52 -0500391LocationCode Manager::getExpandedLocationCode(const LocationCode locationCode,
392 const NodeNumber nodeNumber)
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600393{
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -0500394 ReaderImpl read;
395 return read.getExpandedLocationCode(locationCode, nodeNumber,
396 fruLocationCode);
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600397}
SunnySrivastava1984de3c60d2020-02-03 10:34:33 -0600398
SunnySrivastava19849a195542020-09-07 06:04:50 -0500399void Manager::performVPDRecollection()
400{
401 // get list of FRUs replaceable at standby
402 for (const auto& item : replaceableFrus)
403 {
404 const vector<nlohmann::json>& groupEEPROM = jsonFile["frus"][item];
405 const nlohmann::json& singleFru = groupEEPROM[0];
406
407 const string& inventoryPath =
408 singleFru["inventoryPath"]
409 .get_ref<const nlohmann::json::string_t&>();
410
Santosh Puranikd40e42d2022-03-23 13:58:06 +0530411 bool prePostActionRequired = false;
412
413 if ((jsonFile["frus"][item].at(0)).find("preAction") !=
414 jsonFile["frus"][item].at(0).end())
415 {
416 if (!executePreAction(jsonFile, item))
417 {
418 // if the FRU has preAction defined then its execution should
419 // pass to ensure bind/unbind of data.
420 // preAction execution failed. should not call bind/unbind.
421 log<level::ERR>(
422 "Pre-Action execution failed for the FRU",
423 entry("ERROR=%s",
424 ("Inventory path: " + inventoryPath).c_str()));
425 continue;
426 }
427 prePostActionRequired = true;
428 }
429
SunnySrivastava19849a195542020-09-07 06:04:50 -0500430 if ((singleFru.find("devAddress") == singleFru.end()) ||
431 (singleFru.find("driverType") == singleFru.end()) ||
432 (singleFru.find("busType") == singleFru.end()))
433 {
434 // The FRUs is marked for replacement but missing mandatory
435 // fields for recollection. Skip to another replaceable fru.
436 log<level::ERR>(
437 "Recollection Failed as mandatory field missing in Json",
438 entry("ERROR=%s",
439 ("Recollection failed for " + inventoryPath).c_str()));
440 continue;
441 }
442
443 string str = "echo ";
444 string deviceAddress = singleFru["devAddress"];
445 const string& driverType = singleFru["driverType"];
446 const string& busType = singleFru["busType"];
447
448 // devTreeStatus flag is present in json as false to mention
449 // that the EEPROM is not mentioned in device tree. If this flag
450 // is absent consider the value to be true, i.e EEPROM is
451 // mentioned in device tree
452 if (!singleFru.value("devTreeStatus", true))
453 {
454 auto pos = deviceAddress.find('-');
455 if (pos != string::npos)
456 {
457 string busNum = deviceAddress.substr(0, pos);
458 deviceAddress =
459 "0x" + deviceAddress.substr(pos + 1, string::npos);
460
461 string deleteDevice = str + deviceAddress + " > /sys/bus/" +
462 busType + "/devices/" + busType + "-" +
463 busNum + "/delete_device";
464 executeCmd(deleteDevice);
465
466 string addDevice = str + driverType + " " + deviceAddress +
467 " > /sys/bus/" + busType + "/devices/" +
468 busType + "-" + busNum + "/new_device";
469 executeCmd(addDevice);
470 }
471 else
472 {
473 log<level::ERR>(
474 "Wrong format of device address in Json",
475 entry(
476 "ERROR=%s",
477 ("Recollection failed for " + inventoryPath).c_str()));
478 continue;
479 }
480 }
481 else
482 {
Alpana Kumarib17dd3b2020-10-01 00:18:10 -0500483 executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
484 driverType, "/unbind"));
485 executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
486 driverType, "/bind"));
SunnySrivastava19849a195542020-09-07 06:04:50 -0500487 }
Santosh Puranikd40e42d2022-03-23 13:58:06 +0530488
489 // this check is added to avoid file system expensive call in case not
490 // required.
491 if (prePostActionRequired)
492 {
493 // Check if device showed up (test for file)
494 if (!filesystem::exists(item))
495 {
496 // If not, then take failure postAction
497 executePostFailAction(jsonFile, item);
498 }
499 }
SunnySrivastava19849a195542020-09-07 06:04:50 -0500500 }
501}
502
SunnySrivastava1984b59fd092020-02-03 09:58:56 -0600503} // namespace manager
504} // namespace vpd
Alpana Kumarib17dd3b2020-10-01 00:18:10 -0500505} // namespace openpower