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