blob: ea8bb9729fef5d4f6197f17de48e8eae008638fe [file] [log] [blame]
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +05301#include "config.h"
2
Sunny Srivastava867ee752025-04-15 12:24:23 +05303#include "ibm_handler.hpp"
4
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +05305#include "parser.hpp"
6
Sunny Srivastava78a50422025-04-25 11:17:56 +05307#include <utility/common_utility.hpp>
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +05308#include <utility/dbus_utility.hpp>
9#include <utility/json_utility.hpp>
10#include <utility/vpd_specific_utility.hpp>
11
Sunny Srivastava867ee752025-04-15 12:24:23 +053012namespace vpd
13{
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053014IbmHandler::IbmHandler(
15 std::shared_ptr<Worker>& o_worker,
16 std::shared_ptr<BackupAndRestore>& o_backupAndRestoreObj,
17 const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_iFace,
18 const std::shared_ptr<boost::asio::io_context>& i_ioCon,
19 const std::shared_ptr<sdbusplus::asio::connection>& i_asioConnection) :
20 m_worker(o_worker), m_backupAndRestoreObj(o_backupAndRestoreObj),
21 m_interface(i_iFace), m_ioContext(i_ioCon),
22 m_asioConnection(i_asioConnection)
23{
24 if (dbusUtility::isChassisPowerOn())
25 {
26 // At power on, less number of FRU(s) needs collection. we can scale
27 // down the threads to reduce CPU utilization.
28 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT,
29 constants::VALUE_1);
30 }
31 else
32 {
33 // Initialize with default configuration
34 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
35 }
36
37 // Set up minimal things that is needed before bus name is claimed.
Sunny Srivastava78a50422025-04-25 11:17:56 +053038 performInitialSetup();
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053039
40 if (!m_sysCfgJsonObj.empty() &&
41 jsonUtility::isBackupAndRestoreRequired(m_sysCfgJsonObj))
42 {
43 try
44 {
45 m_backupAndRestoreObj =
46 std::make_shared<BackupAndRestore>(m_sysCfgJsonObj);
47 }
48 catch (const std::exception& l_ex)
49 {
50 logging::logMessage("Back up and restore instantiation failed. {" +
51 std::string(l_ex.what()) + "}");
52
53 EventLogger::createSyncPel(
54 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
55 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
56 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
57 }
58 }
59
Sunny Srivastava380efbb2025-04-25 10:28:30 +053060 // callback to detect host state change.
61 registerHostStateChangeCallback();
62
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053063 // set callback to detect any asset tag change
64 registerAssetTagChangeCallback();
65
66 // set async timer to detect if system VPD is published on D-Bus.
67 SetTimerToDetectSVPDOnDbus();
68
69 // set async timer to detect if VPD collection is done.
70 SetTimerToDetectVpdCollectionStatus();
71
72 // Instantiate GpioMonitor class
73 m_gpioMonitor =
74 std::make_shared<GpioMonitor>(m_sysCfgJsonObj, m_worker, m_ioContext);
75}
76
77void IbmHandler::registerAssetTagChangeCallback()
78{
79 static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
80 std::make_shared<sdbusplus::bus::match_t>(
81 *m_asioConnection,
82 sdbusplus::bus::match::rules::propertiesChanged(
83 constants::systemInvPath, constants::assetTagInf),
84 [this](sdbusplus::message_t& l_msg) {
85 processAssetTagChangeCallback(l_msg);
86 });
87}
88
89void IbmHandler::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
90{
91 try
92 {
93 if (i_msg.is_method_error())
94 {
95 throw std::runtime_error(
96 "Error reading callback msg for asset tag.");
97 }
98
99 std::string l_objectPath;
100 types::PropertyMap l_propMap;
101 i_msg.read(l_objectPath, l_propMap);
102
103 const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
104 if (l_itrToAssetTag != l_propMap.end())
105 {
106 if (auto l_assetTag =
107 std::get_if<std::string>(&(l_itrToAssetTag->second)))
108 {
109 // Call Notify to persist the AssetTag
110 types::ObjectMap l_objectMap = {
111 {sdbusplus::message::object_path(constants::systemInvPath),
112 {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
113
114 // Notify PIM
115 if (!dbusUtility::callPIM(move(l_objectMap)))
116 {
117 throw std::runtime_error(
118 "Call to PIM failed for asset tag update.");
119 }
120 }
121 }
122 else
123 {
124 throw std::runtime_error(
125 "Could not find asset tag in callback message.");
126 }
127 }
128 catch (const std::exception& l_ex)
129 {
130 // TODO: Log PEL with below description.
131 logging::logMessage("Asset tag callback update failed with error: " +
132 std::string(l_ex.what()));
133 }
134}
135
136void IbmHandler::SetTimerToDetectSVPDOnDbus()
137{
138 try
139 {
140 static boost::asio::steady_timer timer(*m_ioContext);
141
142 // timer for 2 seconds
143 auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
144
145 (asyncCancelled == 0) ? logging::logMessage("Timer started")
146 : logging::logMessage("Timer re-started");
147
148 timer.async_wait([this](const boost::system::error_code& ec) {
149 if (ec == boost::asio::error::operation_aborted)
150 {
151 throw std::runtime_error(
152 std::string(__FUNCTION__) +
153 ": Timer to detect system VPD collection status was aborted.");
154 }
155
156 if (ec)
157 {
158 throw std::runtime_error(
159 std::string(__FUNCTION__) +
160 ": Timer to detect System VPD collection failed");
161 }
162
163 if (m_worker->isSystemVPDOnDBus())
164 {
165 // cancel the timer
166 timer.cancel();
167
168 // Triggering FRU VPD collection. Setting status to "In
169 // Progress".
170 m_interface->set_property("CollectionStatus",
171 std::string("InProgress"));
172 m_worker->collectFrusFromJson();
173 }
174 });
175 }
176 catch (const std::exception& l_ex)
177 {
178 EventLogger::createAsyncPel(
179 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
180 __FILE__, __FUNCTION__, 0,
181 std::string("Collection for FRUs failed with reason:") +
182 EventLogger::getErrorMsg(l_ex),
183 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
184 }
185}
186
187void IbmHandler::SetTimerToDetectVpdCollectionStatus()
188{
189 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
190 // system type.
191 static constexpr auto MAX_RETRY = 12;
192
193 static boost::asio::steady_timer l_timer(*m_ioContext);
194 static uint8_t l_timerRetry = 0;
195
196 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
197
198 (l_asyncCancelled == 0)
199 ? logging::logMessage("Collection Timer started")
200 : logging::logMessage("Collection Timer re-started");
201
202 l_timer.async_wait([this](const boost::system::error_code& ec) {
203 if (ec == boost::asio::error::operation_aborted)
204 {
205 throw std::runtime_error(
206 "Timer to detect thread collection status was aborted");
207 }
208
209 if (ec)
210 {
211 throw std::runtime_error(
212 "Timer to detect thread collection failed");
213 }
214
215 if (m_worker->isAllFruCollectionDone())
216 {
217 // cancel the timer
218 l_timer.cancel();
219 processFailedEeproms();
220
221 // update VPD for powerVS system.
222 ConfigurePowerVsSystem();
223
224 std::cout << "m_worker->isSystemVPDOnDBus() completed" << std::endl;
225 m_interface->set_property("CollectionStatus",
226 std::string("Completed"));
227
228 if (m_backupAndRestoreObj)
229 {
230 m_backupAndRestoreObj->backupAndRestore();
231 }
232 }
233 else
234 {
235 auto l_threadCount = m_worker->getActiveThreadCount();
236 if (l_timerRetry == MAX_RETRY)
237 {
238 l_timer.cancel();
239 logging::logMessage("Taking too long. Active thread = " +
240 std::to_string(l_threadCount));
241 }
242 else
243 {
244 l_timerRetry++;
245 logging::logMessage("Collection is in progress for [" +
246 std::to_string(l_threadCount) + "] FRUs.");
247
248 SetTimerToDetectVpdCollectionStatus();
249 }
250 }
251 });
252}
253
254void IbmHandler::checkAndUpdatePowerVsVpd(
255 const nlohmann::json& i_powerVsJsonObj,
256 std::vector<std::string>& o_failedPathList)
257{
258 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
259 {
260 nlohmann::json l_sysCfgJsonObj{};
261 if (m_worker.get() != nullptr)
262 {
263 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
264 }
265
266 // The utility method will handle emty JSON case. No explicit
267 // handling required here.
268 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
269 l_sysCfgJsonObj, l_fruPath);
270
271 // Mark it as failed if inventory path not found in JSON.
272 if (l_inventoryPath.empty())
273 {
274 o_failedPathList.push_back(l_fruPath);
275 continue;
276 }
277
278 // check if the FRU is present
279 if (!dbusUtility::isInventoryPresent(l_inventoryPath))
280 {
281 logging::logMessage(
282 "Inventory not present, skip updating part number. Path: " +
283 l_inventoryPath);
284 continue;
285 }
286
287 // check if the FRU needs CCIN check before updating PN.
288 if (l_recJson.contains("CCIN"))
289 {
290 const auto& l_ccinFromDbus =
291 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath);
292
293 // Not an ideal situation as CCIN can't be empty.
294 if (l_ccinFromDbus.empty())
295 {
296 o_failedPathList.push_back(l_fruPath);
297 continue;
298 }
299
300 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
301
302 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
303 l_ccinFromDbus) == l_ccinListFromJson.end())
304 {
305 // Don't update PN in this case.
306 continue;
307 }
308 }
309
310 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
311 {
312 // Record name can't be CCIN, skip processing as it is there for PN
313 // update based on CCIN check.
314 if (l_recordName == constants::kwdCCIN)
315 {
316 continue;
317 }
318
319 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
320 {
321 // Is value of type array.
322 if (!l_kwdValue.is_array())
323 {
324 o_failedPathList.push_back(l_fruPath);
325 continue;
326 }
327
328 // Get current FRU Part number.
329 auto l_retVal = dbusUtility::readDbusProperty(
330 constants::pimServiceName, l_inventoryPath,
331 constants::viniInf, constants::kwdFN);
332
333 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
334
335 if (!l_ptrToFn)
336 {
337 o_failedPathList.push_back(l_fruPath);
338 continue;
339 }
340
341 types::BinaryVector l_binaryKwdValue =
342 l_kwdValue.get<types::BinaryVector>();
343 if (l_binaryKwdValue == (*l_ptrToFn))
344 {
345 continue;
346 }
347
348 // Update part number only if required.
349 std::shared_ptr<Parser> l_parserObj =
350 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
351 if (l_parserObj->updateVpdKeyword(std::make_tuple(
352 l_recordName, l_kwdName, l_binaryKwdValue)) ==
353 constants::FAILURE)
354 {
355 o_failedPathList.push_back(l_fruPath);
356 continue;
357 }
358
359 // update the Asset interface Spare part number explicitly.
360 if (!dbusUtility::callPIM(types::ObjectMap{
361 {l_inventoryPath,
362 {{constants::assetInf,
363 {{"SparePartNumber",
364 std::string(l_binaryKwdValue.begin(),
365 l_binaryKwdValue.end())}}}}}}))
366 {
367 logging::logMessage(
368 "Updating Spare Part Number under Asset interface failed for path [" +
369 l_inventoryPath + "]");
370 }
371
372 // Just needed for logging.
373 std::string l_initialPartNum((*l_ptrToFn).begin(),
374 (*l_ptrToFn).end());
375 std::string l_finalPartNum(l_binaryKwdValue.begin(),
376 l_binaryKwdValue.end());
377 logging::logMessage(
378 "FRU Part number updated for path [" + l_inventoryPath +
379 "]" + "From [" + l_initialPartNum + "]" + " to [" +
380 l_finalPartNum + "]");
381 }
382 }
383 }
384}
385
386void IbmHandler::ConfigurePowerVsSystem()
387{
388 std::vector<std::string> l_failedPathList;
389 try
390 {
391 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
392 if (l_imValue.empty())
393 {
394 throw DbusException("Invalid IM value read from Dbus");
395 }
396
397 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
398 {
399 // TODO: Should booting be blocked in case of some
400 // misconfigurations?
401 return;
402 }
403
404 const nlohmann::json& l_powerVsJsonObj =
405 jsonUtility::getPowerVsJson(l_imValue);
406
407 if (l_powerVsJsonObj.empty())
408 {
409 throw std::runtime_error("PowerVS Json not found");
410 }
411
412 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
413
414 if (!l_failedPathList.empty())
415 {
416 throw std::runtime_error(
417 "Part number update failed for following paths: ");
418 }
419 }
420 catch (const std::exception& l_ex)
421 {
422 // TODO log appropriate PEL
423 }
424}
425
426void IbmHandler::processFailedEeproms()
427{
428 if (m_worker.get() != nullptr)
429 {
430 // TODO:
431 // - iterate through list of EEPROMs for which thread creation has
432 // failed
433 // - For each failed EEPROM, trigger VPD collection
434 m_worker->getFailedEepromPaths().clear();
435 }
436}
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530437
438void IbmHandler::registerHostStateChangeCallback()
439{
440 static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
441 std::make_shared<sdbusplus::bus::match_t>(
442 *m_asioConnection,
443 sdbusplus::bus::match::rules::propertiesChanged(
444 constants::hostObjectPath, constants::hostInterface),
445 [this](sdbusplus::message_t& i_msg) {
446 hostStateChangeCallBack(i_msg);
447 });
448}
449
450void IbmHandler::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
451{
452 try
453 {
454 if (i_msg.is_method_error())
455 {
456 throw std::runtime_error(
457 "Error reading callback message for host state");
458 }
459
460 std::string l_objectPath;
461 types::PropertyMap l_propMap;
462 i_msg.read(l_objectPath, l_propMap);
463
464 const auto l_itr = l_propMap.find("CurrentHostState");
465
466 if (l_itr == l_propMap.end())
467 {
468 throw std::runtime_error(
469 "CurrentHostState field is missing in callback message");
470 }
471
472 if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
473 {
474 // implies system is moving from standby to power on state
475 if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
476 "TransitioningToRunning")
477 {
478 // TODO: check for all the essential FRUs in the system.
479
480 if (m_worker.get() != nullptr)
481 {
482 // Perform recollection.
483 m_worker->performVpdRecollection();
484 }
485 else
486 {
487 logging::logMessage(
488 "Failed to get worker object, Abort re-collection");
489 }
490 }
491 }
492 else
493 {
494 throw std::runtime_error(
495 "Invalid type recieved in variant for host state.");
496 }
497 }
498 catch (const std::exception& l_ex)
499 {
500 // TODO: Log PEL.
501 logging::logMessage(l_ex.what());
502 }
503}
Sunny Srivastava78a50422025-04-25 11:17:56 +0530504
505void IbmHandler::primeSystemBlueprint()
506{
507 if (m_sysCfgJsonObj.empty())
508 {
509 return;
510 }
511
512 const nlohmann::json& l_listOfFrus =
513 m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
514
515 for (const auto& l_itemFRUS : l_listOfFrus.items())
516 {
517 const std::string& l_vpdFilePath = l_itemFRUS.key();
518
519 if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH)
520 {
521 continue;
522 }
523
524 // Prime the inventry for FRUs which
525 // are not present/processing had some error.
526 if (m_worker.get() != nullptr &&
527 !m_worker->primeInventory(l_vpdFilePath))
528 {
529 logging::logMessage(
530 "Priming of inventory failed for FRU " + l_vpdFilePath);
531 }
532 }
533}
534
535void IbmHandler::enableMuxChips()
536{
537 if (m_sysCfgJsonObj.empty())
538 {
539 // config JSON should not be empty at this point of execution.
540 throw std::runtime_error("Config JSON is empty. Can't enable muxes");
541 return;
542 }
543
544 if (!m_sysCfgJsonObj.contains("muxes"))
545 {
546 logging::logMessage("No mux defined for the system in config JSON");
547 return;
548 }
549
550 // iterate over each MUX detail and enable them.
551 for (const auto& item : m_sysCfgJsonObj["muxes"])
552 {
553 if (item.contains("holdidlepath"))
554 {
555 std::string cmd = "echo 0 > ";
556 cmd += item["holdidlepath"];
557
558 logging::logMessage("Enabling mux with command = " + cmd);
559
560 commonUtility::executeCmd(cmd);
561 continue;
562 }
563
564 logging::logMessage(
565 "Mux Entry does not have hold idle path. Can't enable the mux");
566 }
567}
568
569void IbmHandler::performInitialSetup()
570{
571 try
572 {
573 if (!dbusUtility::isChassisPowerOn())
574 {
575 logging::logMessage("Chassis is in Off state.");
576 if (m_worker.get() != nullptr)
577 {
578 m_worker->setDeviceTreeAndJson();
579 // Get the system config JSON object.
580 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
581 }
582 else
583 {
584 throw std::runtime_error(
585 "Worker object not found. Can't set up device tree and Json.");
586 }
587 primeSystemBlueprint();
588 }
589 else
590 {
591 // get the JSON from worker.
592 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
593 }
594
595 // Enable all mux which are used for connecting to the i2c on the
596 // pcie slots for pcie cards. These are not enabled by kernel due to
597 // an issue seen with Castello cards, where the i2c line hangs on a
598 // probe.
599 enableMuxChips();
600
601 // Nothing needs to be done. Service restarted or BMC re-booted for
602 // some reason at system power on.
603 return;
604 }
605 catch (const std::exception& l_ex)
606 {
607 // Any issue in system's inital set up is handled in this catch. Error
608 // will not propogate to manager.
609 EventLogger::createSyncPel(
610 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
611 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
612 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
613 }
614}
615
Sunny Srivastava867ee752025-04-15 12:24:23 +0530616} // namespace vpd