1 #include "config.h"
2
3 #include "ibm_handler.hpp"
4
5 #include "listener.hpp"
6 #include "parser.hpp"
7
8 #include <utility/common_utility.hpp>
9 #include <utility/dbus_utility.hpp>
10 #include <utility/json_utility.hpp>
11 #include <utility/vpd_specific_utility.hpp>
12
13 namespace vpd
14 {
IbmHandler(std::shared_ptr<Worker> & o_worker,std::shared_ptr<BackupAndRestore> & o_backupAndRestoreObj,const std::shared_ptr<sdbusplus::asio::dbus_interface> & i_iFace,const std::shared_ptr<sdbusplus::asio::dbus_interface> & i_progressiFace,const std::shared_ptr<boost::asio::io_context> & i_ioCon,const std::shared_ptr<sdbusplus::asio::connection> & i_asioConnection)15 IbmHandler::IbmHandler(
16 std::shared_ptr<Worker>& o_worker,
17 std::shared_ptr<BackupAndRestore>& o_backupAndRestoreObj,
18 const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_iFace,
19 const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_progressiFace,
20 const std::shared_ptr<boost::asio::io_context>& i_ioCon,
21 const std::shared_ptr<sdbusplus::asio::connection>& i_asioConnection) :
22 m_worker(o_worker), m_backupAndRestoreObj(o_backupAndRestoreObj),
23 m_interface(i_iFace), m_progressInterface(i_progressiFace),
24 m_ioContext(i_ioCon), m_asioConnection(i_asioConnection),
25 m_logger(Logger::getLoggerInstance())
26 {
27 uint16_t l_errCode{0};
28
29 // check VPD collection mode
30 const auto l_vpdCollectionMode =
31 commonUtility::isFieldModeEnabled()
32 ? types::VpdCollectionMode::DEFAULT_MODE
33 : commonUtility::getVpdCollectionMode(l_errCode);
34
35 if (l_errCode)
36 {
37 m_logger->logMessage(
38 "Error while trying to read VPD collection mode: " +
39 commonUtility::getErrCodeMsg(l_errCode));
40 }
41
42 if (dbusUtility::isChassisPowerOn())
43 {
44 // At power on, less number of FRU(s) needs collection. we can scale
45 // down the threads to reduce CPU utilization.
46 m_worker = std::make_shared<Worker>(
47 INVENTORY_JSON_DEFAULT, constants::VALUE_1, l_vpdCollectionMode);
48 }
49 else
50 {
51 // Initialize with default configuration
52 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT,
53 constants::MAX_THREADS,
54 l_vpdCollectionMode);
55 }
56
57 // Set up minimal things that is needed before bus name is claimed.
58 performInitialSetup();
59
60 if (!m_sysCfgJsonObj.empty() &&
61 jsonUtility::isBackupAndRestoreRequired(m_sysCfgJsonObj, l_errCode))
62 {
63 try
64 {
65 m_backupAndRestoreObj =
66 std::make_shared<BackupAndRestore>(m_sysCfgJsonObj);
67 }
68 catch (const std::exception& l_ex)
69 {
70 logging::logMessage("Back up and restore instantiation failed. {" +
71 std::string(l_ex.what()) + "}");
72
73 EventLogger::createSyncPel(
74 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
75 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
76 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
77 }
78 }
79 else if (l_errCode)
80 {
81 logging::logMessage(
82 "Failed to check if backup & restore required. Error : " +
83 commonUtility::getErrCodeMsg(l_errCode));
84 }
85
86 // Instantiate Listener object
87 m_eventListener = std::make_shared<Listener>(m_worker, m_asioConnection);
88 m_eventListener->registerAssetTagChangeCallback();
89 m_eventListener->registerHostStateChangeCallback();
90 m_eventListener->registerPresenceChangeCallback();
91
92 // Instantiate GpioMonitor class
93 m_gpioMonitor =
94 std::make_shared<GpioMonitor>(m_sysCfgJsonObj, m_worker, m_ioContext);
95 }
96
SetTimerToDetectVpdCollectionStatus()97 void IbmHandler::SetTimerToDetectVpdCollectionStatus()
98 {
99 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
100 // system type.
101 static constexpr auto MAX_RETRY = 12;
102
103 static boost::asio::steady_timer l_timer(*m_ioContext);
104 static uint8_t l_timerRetry = 0;
105
106 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
107
108 (l_asyncCancelled == 0)
109 ? logging::logMessage("Collection Timer started")
110 : logging::logMessage("Collection Timer re-started");
111
112 l_timer.async_wait([this](const boost::system::error_code& ec) {
113 if (ec == boost::asio::error::operation_aborted)
114 {
115 throw std::runtime_error(
116 "Timer to detect thread collection status was aborted");
117 }
118
119 if (ec)
120 {
121 throw std::runtime_error(
122 "Timer to detect thread collection failed");
123 }
124
125 if (m_worker->isAllFruCollectionDone())
126 {
127 // cancel the timer
128 l_timer.cancel();
129 processFailedEeproms();
130
131 // update VPD for powerVS system.
132 ConfigurePowerVsSystem();
133
134 std::cout << "m_worker->isSystemVPDOnDBus() completed" << std::endl;
135 m_progressInterface->set_property(
136 "Status", std::string(constants::vpdCollectionCompleted));
137
138 if (m_backupAndRestoreObj)
139 {
140 m_backupAndRestoreObj->backupAndRestore();
141 }
142
143 if (m_eventListener)
144 {
145 m_eventListener->registerCorrPropCallBack();
146 }
147
148 // terminate collection logger
149 m_logger->terminateVpdCollectionLogging();
150 }
151 else
152 {
153 auto l_threadCount = m_worker->getActiveThreadCount();
154 if (l_timerRetry == MAX_RETRY)
155 {
156 l_timer.cancel();
157 logging::logMessage("Taking too long. Active thread = " +
158 std::to_string(l_threadCount));
159
160 // terminate collection logger
161 m_logger->terminateVpdCollectionLogging();
162 }
163 else
164 {
165 l_timerRetry++;
166 logging::logMessage("Collection is in progress for [" +
167 std::to_string(l_threadCount) + "] FRUs.");
168
169 SetTimerToDetectVpdCollectionStatus();
170 }
171 }
172 });
173 }
174
checkAndUpdatePowerVsVpd(const nlohmann::json & i_powerVsJsonObj,std::vector<std::string> & o_failedPathList)175 void IbmHandler::checkAndUpdatePowerVsVpd(
176 const nlohmann::json& i_powerVsJsonObj,
177 std::vector<std::string>& o_failedPathList)
178 {
179 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
180 {
181 nlohmann::json l_sysCfgJsonObj{};
182 if (m_worker.get() != nullptr)
183 {
184 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
185 }
186
187 // The utility method will handle emty JSON case. No explicit
188 // handling required here.
189 uint16_t l_errCode = 0;
190 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
191 l_sysCfgJsonObj, l_fruPath, l_errCode);
192
193 // Mark it as failed if inventory path not found in JSON.
194 if (l_inventoryPath.empty())
195 {
196 if (l_errCode)
197 {
198 logging::logMessage(
199 "Failed to get inventory object path from JSON for FRU [" +
200 l_fruPath +
201 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
202 }
203
204 o_failedPathList.push_back(l_fruPath);
205 continue;
206 }
207
208 // check if the FRU is present
209 if (!dbusUtility::isInventoryPresent(l_inventoryPath))
210 {
211 logging::logMessage(
212 "Inventory not present, skip updating part number. Path: " +
213 l_inventoryPath);
214 continue;
215 }
216
217 // check if the FRU needs CCIN check before updating PN.
218 if (l_recJson.contains("CCIN"))
219 {
220 const auto& l_ccinFromDbus =
221 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath, l_errCode);
222
223 // Not an ideal situation as CCIN can't be empty.
224 if (l_ccinFromDbus.empty())
225 {
226 if (l_errCode)
227 {
228 m_logger->logMessage(
229 "Failed to get CCIN value from DBus, error : " +
230 commonUtility::getErrCodeMsg(l_errCode));
231 }
232
233 o_failedPathList.push_back(l_fruPath);
234 continue;
235 }
236
237 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
238
239 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
240 l_ccinFromDbus) == l_ccinListFromJson.end())
241 {
242 // Don't update PN in this case.
243 continue;
244 }
245 }
246
247 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
248 {
249 // Record name can't be CCIN, skip processing as it is there for PN
250 // update based on CCIN check.
251 if (l_recordName == constants::kwdCCIN)
252 {
253 continue;
254 }
255
256 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
257 {
258 // Is value of type array.
259 if (!l_kwdValue.is_array())
260 {
261 o_failedPathList.push_back(l_fruPath);
262 continue;
263 }
264
265 // Get current FRU Part number.
266 auto l_retVal = dbusUtility::readDbusProperty(
267 constants::pimServiceName, l_inventoryPath,
268 constants::viniInf, constants::kwdFN);
269
270 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
271
272 if (!l_ptrToFn)
273 {
274 o_failedPathList.push_back(l_fruPath);
275 continue;
276 }
277
278 types::BinaryVector l_binaryKwdValue =
279 l_kwdValue.get<types::BinaryVector>();
280 if (l_binaryKwdValue == (*l_ptrToFn))
281 {
282 continue;
283 }
284
285 // Update part number only if required.
286 std::shared_ptr<Parser> l_parserObj =
287 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
288 if (l_parserObj->updateVpdKeyword(std::make_tuple(
289 l_recordName, l_kwdName, l_binaryKwdValue)) ==
290 constants::FAILURE)
291 {
292 o_failedPathList.push_back(l_fruPath);
293 continue;
294 }
295
296 // update the Asset interface Spare part number explicitly.
297 if (!dbusUtility::callPIM(types::ObjectMap{
298 {l_inventoryPath,
299 {{constants::assetInf,
300 {{"SparePartNumber",
301 std::string(l_binaryKwdValue.begin(),
302 l_binaryKwdValue.end())}}}}}}))
303 {
304 logging::logMessage(
305 "Updating Spare Part Number under Asset interface failed for path [" +
306 l_inventoryPath + "]");
307 }
308
309 // Just needed for logging.
310 std::string l_initialPartNum((*l_ptrToFn).begin(),
311 (*l_ptrToFn).end());
312 std::string l_finalPartNum(l_binaryKwdValue.begin(),
313 l_binaryKwdValue.end());
314 logging::logMessage(
315 "FRU Part number updated for path [" + l_inventoryPath +
316 "]" + "From [" + l_initialPartNum + "]" + " to [" +
317 l_finalPartNum + "]");
318 }
319 }
320 }
321 }
322
ConfigurePowerVsSystem()323 void IbmHandler::ConfigurePowerVsSystem()
324 {
325 std::vector<std::string> l_failedPathList;
326 try
327 {
328 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
329 if (l_imValue.empty())
330 {
331 throw DbusException("Invalid IM value read from Dbus");
332 }
333
334 uint16_t l_errCode = 0;
335 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue, l_errCode))
336 {
337 // TODO: Should booting be blocked in case of some
338 // misconfigurations?
339 if (l_errCode)
340 {
341 logging::logMessage(
342 "Failed to check if the system is powerVs Configuration, error : " +
343 commonUtility::getErrCodeMsg(l_errCode));
344 }
345
346 return;
347 }
348
349 const nlohmann::json& l_powerVsJsonObj =
350 jsonUtility::getPowerVsJson(l_imValue, l_errCode);
351
352 if (l_powerVsJsonObj.empty())
353 {
354 throw std::runtime_error("PowerVS Json not found. Error : " +
355 commonUtility::getErrCodeMsg(l_errCode));
356 }
357
358 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
359
360 if (!l_failedPathList.empty())
361 {
362 throw std::runtime_error(
363 "Part number update failed for following paths: ");
364 }
365 }
366 catch (const std::exception& l_ex)
367 {
368 // TODO log appropriate PEL
369 }
370 }
371
processFailedEeproms()372 void IbmHandler::processFailedEeproms()
373 {
374 if (m_worker.get() != nullptr)
375 {
376 // TODO:
377 // - iterate through list of EEPROMs for which thread creation has
378 // failed
379 // - For each failed EEPROM, trigger VPD collection
380 m_worker->getFailedEepromPaths().clear();
381 }
382 }
383
enableMuxChips()384 void IbmHandler::enableMuxChips()
385 {
386 if (m_sysCfgJsonObj.empty())
387 {
388 // config JSON should not be empty at this point of execution.
389 throw std::runtime_error("Config JSON is empty. Can't enable muxes");
390 return;
391 }
392
393 if (!m_sysCfgJsonObj.contains("muxes"))
394 {
395 logging::logMessage("No mux defined for the system in config JSON");
396 return;
397 }
398
399 // iterate over each MUX detail and enable them.
400 for (const auto& item : m_sysCfgJsonObj["muxes"])
401 {
402 if (item.contains("holdidlepath"))
403 {
404 std::string cmd = "echo 0 > ";
405 cmd += item["holdidlepath"];
406
407 logging::logMessage("Enabling mux with command = " + cmd);
408
409 commonUtility::executeCmd(cmd);
410 continue;
411 }
412
413 logging::logMessage(
414 "Mux Entry does not have hold idle path. Can't enable the mux");
415 }
416 }
417
performInitialSetup()418 void IbmHandler::performInitialSetup()
419 {
420 try
421 {
422 if (m_worker.get() == nullptr)
423 {
424 throw std::runtime_error(
425 "Worker object not found. Can't perform initial setup.");
426 }
427
428 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
429 if (!dbusUtility::isChassisPowerOn())
430 {
431 m_worker->setDeviceTreeAndJson();
432
433 // Since the above function setDeviceTreeAndJson can change the json
434 // which is used, we would need to reacquire the json object again
435 // here.
436 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
437 }
438
439 // Update BMC postion for RBMC prototype system
440 // Ignore BMC position update in case of any error
441 uint16_t l_errCode = 0;
442 if (isRbmcPrototypeSystem(l_errCode))
443 {
444 size_t l_bmcPosition = std::numeric_limits<size_t>::max();
445 checkAndUpdateBmcPosition(l_bmcPosition);
446
447 if (dbusUtility::callPIM(types::ObjectMap{
448 {sdbusplus::message::object_path(constants::systemInvPath),
449 {{constants::rbmcPositionInterface,
450 {{"Position", l_bmcPosition}}}}}}))
451 {
452 m_logger->logMessage(
453 "Updating BMC position failed for path [" +
454 std::string(constants::systemInvPath) +
455 "], bmc position: " + std::to_string(l_bmcPosition));
456
457 // ToDo: Check is PEL required
458 }
459 }
460 else if (l_errCode != 0)
461 {
462 m_logger->logMessage(
463 "Unable to determine whether system is RBMC system or not, reason: " +
464 commonUtility::getErrCodeMsg(l_errCode));
465 }
466
467 // Enable all mux which are used for connecting to the i2c on the
468 // pcie slots for pcie cards. These are not enabled by kernel due to
469 // an issue seen with Castello cards, where the i2c line hangs on a
470 // probe.
471 enableMuxChips();
472
473 // Nothing needs to be done. Service restarted or BMC re-booted for
474 // some reason at system power on.
475 }
476 catch (const std::exception& l_ex)
477 {
478 m_worker->setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
479 constants::vpdCollectionFailed);
480 // Any issue in system's inital set up is handled in this catch. Error
481 // will not propogate to manager.
482 EventLogger::createSyncPel(
483 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
484 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
485 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
486 }
487 }
488
collectAllFruVpd()489 void IbmHandler::collectAllFruVpd()
490 {
491 // initialize VPD collection logger
492 m_logger->initiateVpdCollectionLogging();
493
494 // Setting status to "InProgress", before trigeering VPD collection.
495 m_progressInterface->set_property(
496 "Status", std::string(constants::vpdCollectionInProgress));
497 m_worker->collectFrusFromJson();
498 SetTimerToDetectVpdCollectionStatus();
499 }
500
isRbmcPrototypeSystem(uint16_t & o_errCode) const501 bool IbmHandler::isRbmcPrototypeSystem(uint16_t& o_errCode) const noexcept
502 {
503 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
504 if (l_imValue.empty())
505 {
506 o_errCode = error_code::DBUS_FAILURE;
507 return false;
508 }
509
510 if (constants::rbmcPrototypeSystemImValue == l_imValue)
511 {
512 return true;
513 }
514
515 return false;
516 }
517
checkAndUpdateBmcPosition(size_t & o_bmcPosition) const518 void IbmHandler::checkAndUpdateBmcPosition(size_t& o_bmcPosition) const noexcept
519 {
520 if (m_worker.get() == nullptr)
521 {
522 m_logger->logMessage("Worker object not found");
523 return;
524 }
525
526 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
527 if (l_sysCfgJsonObj.empty())
528 {
529 m_logger->logMessage(
530 "System config JSON is empty, unable to find BMC position");
531 return;
532 }
533
534 uint16_t l_errCode = 0;
535 std::string l_motherboardEepromPath = jsonUtility::getFruPathFromJson(
536 l_sysCfgJsonObj, constants::systemVpdInvPath, l_errCode);
537
538 if (!l_motherboardEepromPath.empty())
539 {
540 o_bmcPosition = constants::VALUE_1;
541 std::error_code l_ec;
542 if (std::filesystem::exists(l_motherboardEepromPath, l_ec))
543 {
544 o_bmcPosition = constants::VALUE_0;
545 }
546 }
547 else if (l_errCode)
548 {
549 m_logger->logMessage("Unable to determine BMC position, reason: " +
550 commonUtility::getErrCodeMsg(l_errCode));
551 }
552 else
553 {
554 m_logger->logMessage("Unable to determine BMC position, as FRU path[" +
555 std::string(constants::systemVpdInvPath) +
556 "], not found in the system config JSON.");
557 }
558 }
559 } // namespace vpd
560