blob: ae62a70e112a48e7a248df9e61f7e3a80a9af0b6 [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),
Souvik Roya5e18b82025-09-25 05:59:56 +000024 m_ioContext(i_ioCon), m_asioConnection(i_asioConnection),
25 m_logger(Logger::getLoggerInstance())
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053026{
27 if (dbusUtility::isChassisPowerOn())
28 {
29 // At power on, less number of FRU(s) needs collection. we can scale
30 // down the threads to reduce CPU utilization.
31 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT,
32 constants::VALUE_1);
33 }
34 else
35 {
36 // Initialize with default configuration
37 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
38 }
39
40 // Set up minimal things that is needed before bus name is claimed.
Sunny Srivastava78a50422025-04-25 11:17:56 +053041 performInitialSetup();
Rekha Aparna196e3082025-09-08 20:40:35 -050042 uint16_t l_errCode = 0;
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053043
44 if (!m_sysCfgJsonObj.empty() &&
Rekha Aparna196e3082025-09-08 20:40:35 -050045 jsonUtility::isBackupAndRestoreRequired(m_sysCfgJsonObj, l_errCode))
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053046 {
47 try
48 {
49 m_backupAndRestoreObj =
50 std::make_shared<BackupAndRestore>(m_sysCfgJsonObj);
51 }
52 catch (const std::exception& l_ex)
53 {
54 logging::logMessage("Back up and restore instantiation failed. {" +
55 std::string(l_ex.what()) + "}");
56
57 EventLogger::createSyncPel(
58 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
59 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
60 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
61 }
62 }
Rekha Aparna196e3082025-09-08 20:40:35 -050063 else if (l_errCode)
64 {
65 logging::logMessage(
66 "Failed to check if backup & restore required. Error : " +
Rekha Aparnac6159a22025-10-09 12:20:20 +053067 commonUtility::getErrCodeMsg(l_errCode));
Rekha Aparna196e3082025-09-08 20:40:35 -050068 }
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053069
Anupama B Rc7565ed2025-06-19 02:08:39 -050070 // Instantiate Listener object
Anupama B R56e45372025-06-19 12:54:44 -050071 m_eventListener = std::make_shared<Listener>(m_worker, m_asioConnection);
72 m_eventListener->registerAssetTagChangeCallback();
73 m_eventListener->registerHostStateChangeCallback();
Souvik Roy5c3a1562025-07-02 01:39:44 -050074 m_eventListener->registerPresenceChangeCallback();
Anupama B Rc7565ed2025-06-19 02:08:39 -050075
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053076 // Instantiate GpioMonitor class
77 m_gpioMonitor =
78 std::make_shared<GpioMonitor>(m_sysCfgJsonObj, m_worker, m_ioContext);
79}
80
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053081void IbmHandler::SetTimerToDetectVpdCollectionStatus()
82{
83 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
84 // system type.
85 static constexpr auto MAX_RETRY = 12;
86
87 static boost::asio::steady_timer l_timer(*m_ioContext);
88 static uint8_t l_timerRetry = 0;
89
90 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
91
92 (l_asyncCancelled == 0)
93 ? logging::logMessage("Collection Timer started")
94 : logging::logMessage("Collection Timer re-started");
95
96 l_timer.async_wait([this](const boost::system::error_code& ec) {
97 if (ec == boost::asio::error::operation_aborted)
98 {
99 throw std::runtime_error(
100 "Timer to detect thread collection status was aborted");
101 }
102
103 if (ec)
104 {
105 throw std::runtime_error(
106 "Timer to detect thread collection failed");
107 }
108
109 if (m_worker->isAllFruCollectionDone())
110 {
111 // cancel the timer
112 l_timer.cancel();
113 processFailedEeproms();
114
115 // update VPD for powerVS system.
116 ConfigurePowerVsSystem();
117
118 std::cout << "m_worker->isSystemVPDOnDBus() completed" << std::endl;
Anupama B Rda9806b2025-08-29 02:41:10 -0500119 m_progressInterface->set_property(
120 "Status", std::string(constants::vpdCollectionCompleted));
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530121
122 if (m_backupAndRestoreObj)
123 {
124 m_backupAndRestoreObj->backupAndRestore();
125 }
Souvik Roy3d4af4a2025-06-23 04:45:27 -0500126
127 if (m_eventListener)
128 {
129 m_eventListener->registerCorrPropCallBack();
130 }
Souvik Roya5e18b82025-09-25 05:59:56 +0000131
132 // terminate collection logger
133 m_logger->terminateVpdCollectionLogging();
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530134 }
135 else
136 {
137 auto l_threadCount = m_worker->getActiveThreadCount();
138 if (l_timerRetry == MAX_RETRY)
139 {
140 l_timer.cancel();
141 logging::logMessage("Taking too long. Active thread = " +
142 std::to_string(l_threadCount));
Souvik Roya5e18b82025-09-25 05:59:56 +0000143
144 // terminate collection logger
145 m_logger->terminateVpdCollectionLogging();
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530146 }
147 else
148 {
149 l_timerRetry++;
150 logging::logMessage("Collection is in progress for [" +
151 std::to_string(l_threadCount) + "] FRUs.");
152
153 SetTimerToDetectVpdCollectionStatus();
154 }
155 }
156 });
157}
158
159void IbmHandler::checkAndUpdatePowerVsVpd(
160 const nlohmann::json& i_powerVsJsonObj,
161 std::vector<std::string>& o_failedPathList)
162{
163 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
164 {
165 nlohmann::json l_sysCfgJsonObj{};
166 if (m_worker.get() != nullptr)
167 {
168 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
169 }
170
171 // The utility method will handle emty JSON case. No explicit
172 // handling required here.
Rekha Aparna017567a2025-08-13 02:07:06 -0500173 uint16_t l_errCode = 0;
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530174 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
Rekha Aparna017567a2025-08-13 02:07:06 -0500175 l_sysCfgJsonObj, l_fruPath, l_errCode);
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530176
177 // Mark it as failed if inventory path not found in JSON.
178 if (l_inventoryPath.empty())
179 {
Rekha Aparna017567a2025-08-13 02:07:06 -0500180 if (l_errCode)
181 {
182 logging::logMessage(
183 "Failed to get inventory object path from JSON for FRU [" +
Rekha Aparnac6159a22025-10-09 12:20:20 +0530184 l_fruPath +
185 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
Rekha Aparna017567a2025-08-13 02:07:06 -0500186 }
187
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530188 o_failedPathList.push_back(l_fruPath);
189 continue;
190 }
191
192 // check if the FRU is present
193 if (!dbusUtility::isInventoryPresent(l_inventoryPath))
194 {
195 logging::logMessage(
196 "Inventory not present, skip updating part number. Path: " +
197 l_inventoryPath);
198 continue;
199 }
200
201 // check if the FRU needs CCIN check before updating PN.
202 if (l_recJson.contains("CCIN"))
203 {
204 const auto& l_ccinFromDbus =
205 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath);
206
207 // Not an ideal situation as CCIN can't be empty.
208 if (l_ccinFromDbus.empty())
209 {
210 o_failedPathList.push_back(l_fruPath);
211 continue;
212 }
213
214 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
215
216 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
217 l_ccinFromDbus) == l_ccinListFromJson.end())
218 {
219 // Don't update PN in this case.
220 continue;
221 }
222 }
223
224 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
225 {
226 // Record name can't be CCIN, skip processing as it is there for PN
227 // update based on CCIN check.
228 if (l_recordName == constants::kwdCCIN)
229 {
230 continue;
231 }
232
233 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
234 {
235 // Is value of type array.
236 if (!l_kwdValue.is_array())
237 {
238 o_failedPathList.push_back(l_fruPath);
239 continue;
240 }
241
242 // Get current FRU Part number.
243 auto l_retVal = dbusUtility::readDbusProperty(
244 constants::pimServiceName, l_inventoryPath,
245 constants::viniInf, constants::kwdFN);
246
247 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
248
249 if (!l_ptrToFn)
250 {
251 o_failedPathList.push_back(l_fruPath);
252 continue;
253 }
254
255 types::BinaryVector l_binaryKwdValue =
256 l_kwdValue.get<types::BinaryVector>();
257 if (l_binaryKwdValue == (*l_ptrToFn))
258 {
259 continue;
260 }
261
262 // Update part number only if required.
263 std::shared_ptr<Parser> l_parserObj =
264 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
265 if (l_parserObj->updateVpdKeyword(std::make_tuple(
266 l_recordName, l_kwdName, l_binaryKwdValue)) ==
267 constants::FAILURE)
268 {
269 o_failedPathList.push_back(l_fruPath);
270 continue;
271 }
272
273 // update the Asset interface Spare part number explicitly.
274 if (!dbusUtility::callPIM(types::ObjectMap{
275 {l_inventoryPath,
276 {{constants::assetInf,
277 {{"SparePartNumber",
278 std::string(l_binaryKwdValue.begin(),
279 l_binaryKwdValue.end())}}}}}}))
280 {
281 logging::logMessage(
282 "Updating Spare Part Number under Asset interface failed for path [" +
283 l_inventoryPath + "]");
284 }
285
286 // Just needed for logging.
287 std::string l_initialPartNum((*l_ptrToFn).begin(),
288 (*l_ptrToFn).end());
289 std::string l_finalPartNum(l_binaryKwdValue.begin(),
290 l_binaryKwdValue.end());
291 logging::logMessage(
292 "FRU Part number updated for path [" + l_inventoryPath +
293 "]" + "From [" + l_initialPartNum + "]" + " to [" +
294 l_finalPartNum + "]");
295 }
296 }
297 }
298}
299
300void IbmHandler::ConfigurePowerVsSystem()
301{
302 std::vector<std::string> l_failedPathList;
303 try
304 {
305 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
306 if (l_imValue.empty())
307 {
308 throw DbusException("Invalid IM value read from Dbus");
309 }
310
311 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
312 {
313 // TODO: Should booting be blocked in case of some
314 // misconfigurations?
315 return;
316 }
317
Rekha Aparnaadf85262025-09-09 00:41:00 -0500318 uint16_t l_errCode = 0;
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530319 const nlohmann::json& l_powerVsJsonObj =
Rekha Aparnaadf85262025-09-09 00:41:00 -0500320 jsonUtility::getPowerVsJson(l_imValue, l_errCode);
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530321
322 if (l_powerVsJsonObj.empty())
323 {
Rekha Aparnac6159a22025-10-09 12:20:20 +0530324 throw std::runtime_error("PowerVS Json not found. Error : " +
325 commonUtility::getErrCodeMsg(l_errCode));
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530326 }
327
328 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
329
330 if (!l_failedPathList.empty())
331 {
332 throw std::runtime_error(
333 "Part number update failed for following paths: ");
334 }
335 }
336 catch (const std::exception& l_ex)
337 {
338 // TODO log appropriate PEL
339 }
340}
341
342void IbmHandler::processFailedEeproms()
343{
344 if (m_worker.get() != nullptr)
345 {
346 // TODO:
347 // - iterate through list of EEPROMs for which thread creation has
348 // failed
349 // - For each failed EEPROM, trigger VPD collection
350 m_worker->getFailedEepromPaths().clear();
351 }
352}
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530353
Sunny Srivastava78a50422025-04-25 11:17:56 +0530354void IbmHandler::primeSystemBlueprint()
355{
356 if (m_sysCfgJsonObj.empty())
357 {
358 return;
359 }
360
361 const nlohmann::json& l_listOfFrus =
362 m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
363
364 for (const auto& l_itemFRUS : l_listOfFrus.items())
365 {
366 const std::string& l_vpdFilePath = l_itemFRUS.key();
367
368 if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH)
369 {
370 continue;
371 }
372
373 // Prime the inventry for FRUs which
374 // are not present/processing had some error.
375 if (m_worker.get() != nullptr &&
376 !m_worker->primeInventory(l_vpdFilePath))
377 {
378 logging::logMessage(
379 "Priming of inventory failed for FRU " + l_vpdFilePath);
380 }
381 }
382}
383
384void 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
418void IbmHandler::performInitialSetup()
419{
420 try
421 {
Anupama B R281e2d42025-05-05 10:05:13 -0500422 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();
Sunny Srivastava78a50422025-04-25 11:17:56 +0530429 if (!dbusUtility::isChassisPowerOn())
430 {
Anupama B R281e2d42025-05-05 10:05:13 -0500431 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.
Sunny Srivastava78a50422025-04-25 11:17:56 +0530436 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
Anupama B R281e2d42025-05-05 10:05:13 -0500437
Anupama B Rec7f8862025-05-23 01:21:01 -0500438 if (isPrimingRequired())
439 {
440 primeSystemBlueprint();
441 }
Sunny Srivastava78a50422025-04-25 11:17:56 +0530442 }
443
444 // Enable all mux which are used for connecting to the i2c on the
445 // pcie slots for pcie cards. These are not enabled by kernel due to
446 // an issue seen with Castello cards, where the i2c line hangs on a
447 // probe.
448 enableMuxChips();
449
450 // Nothing needs to be done. Service restarted or BMC re-booted for
451 // some reason at system power on.
Sunny Srivastava78a50422025-04-25 11:17:56 +0530452 }
453 catch (const std::exception& l_ex)
454 {
Anupama B R4c65fcd2025-09-01 08:09:00 -0500455 m_worker->setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
456 constants::vpdCollectionFailed);
Sunny Srivastava78a50422025-04-25 11:17:56 +0530457 // Any issue in system's inital set up is handled in this catch. Error
458 // will not propogate to manager.
459 EventLogger::createSyncPel(
460 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
461 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
462 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
463 }
464}
465
Anupama B Rec7f8862025-05-23 01:21:01 -0500466bool IbmHandler::isPrimingRequired() const noexcept
467{
468 try
469 {
470 // get all object paths under PIM
471 const auto l_objectPaths = dbusUtility::GetSubTreePaths(
472 constants::systemInvPath, 0,
473 std::vector<std::string>{constants::vpdCollectionInterface});
474
475 const nlohmann::json& l_listOfFrus =
476 m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
477
478 size_t l_invPathCount = 0;
479
480 for (const auto& l_itemFRUS : l_listOfFrus.items())
481 {
482 for (const auto& l_Fru : l_itemFRUS.value())
483 {
484 if (l_Fru.contains("ccin") || (l_Fru.contains("noprime") &&
485 l_Fru.value("noprime", false)))
486 {
487 continue;
488 }
489
490 l_invPathCount += 1;
491 }
492 }
493 return ((l_objectPaths.size() >= l_invPathCount) ? false : true);
494 }
495 catch (const std::exception& l_ex)
496 {
497 logging::logMessage(
498 "Error while checking is priming required or not, error: " +
499 std::string(l_ex.what()));
500 }
501
502 // In case of any error, perform priming, as it's unclear whether priming is
503 // required.
504 return true;
505}
Anupama B R7127ab42025-06-26 01:39:08 -0500506
507void IbmHandler::collectAllFruVpd()
508{
Souvik Roya5e18b82025-09-25 05:59:56 +0000509 // initialize VPD collection logger
510 m_logger->initiateVpdCollectionLogging();
511
Anupama B R7127ab42025-06-26 01:39:08 -0500512 // Setting status to "InProgress", before trigeering VPD collection.
Anupama B Rda9806b2025-08-29 02:41:10 -0500513 m_progressInterface->set_property(
514 "Status", std::string(constants::vpdCollectionInProgress));
Anupama B R7127ab42025-06-26 01:39:08 -0500515 m_worker->collectFrusFromJson();
516 SetTimerToDetectVpdCollectionStatus();
517}
Sunny Srivastava867ee752025-04-15 12:24:23 +0530518} // namespace vpd