blob: 436c617eb77b31b9e225b66785b7df5e244cc1df [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();
Rekha Aparna196e3082025-09-08 20:40:35 -050041 uint16_t l_errCode = 0;
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053042
43 if (!m_sysCfgJsonObj.empty() &&
Rekha Aparna196e3082025-09-08 20:40:35 -050044 jsonUtility::isBackupAndRestoreRequired(m_sysCfgJsonObj, l_errCode))
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053045 {
46 try
47 {
48 m_backupAndRestoreObj =
49 std::make_shared<BackupAndRestore>(m_sysCfgJsonObj);
50 }
51 catch (const std::exception& l_ex)
52 {
53 logging::logMessage("Back up and restore instantiation failed. {" +
54 std::string(l_ex.what()) + "}");
55
56 EventLogger::createSyncPel(
57 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
58 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
59 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
60 }
61 }
Rekha Aparna196e3082025-09-08 20:40:35 -050062 else if (l_errCode)
63 {
64 logging::logMessage(
65 "Failed to check if backup & restore required. Error : " +
66 vpdSpecificUtility::getErrCodeMsg(l_errCode));
67 }
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053068
Anupama B Rc7565ed2025-06-19 02:08:39 -050069 // Instantiate Listener object
Anupama B R56e45372025-06-19 12:54:44 -050070 m_eventListener = std::make_shared<Listener>(m_worker, m_asioConnection);
71 m_eventListener->registerAssetTagChangeCallback();
72 m_eventListener->registerHostStateChangeCallback();
Souvik Roy5c3a1562025-07-02 01:39:44 -050073 m_eventListener->registerPresenceChangeCallback();
Anupama B Rc7565ed2025-06-19 02:08:39 -050074
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053075 // Instantiate GpioMonitor class
76 m_gpioMonitor =
77 std::make_shared<GpioMonitor>(m_sysCfgJsonObj, m_worker, m_ioContext);
78}
79
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053080void IbmHandler::SetTimerToDetectVpdCollectionStatus()
81{
82 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
83 // system type.
84 static constexpr auto MAX_RETRY = 12;
85
86 static boost::asio::steady_timer l_timer(*m_ioContext);
87 static uint8_t l_timerRetry = 0;
88
89 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
90
91 (l_asyncCancelled == 0)
92 ? logging::logMessage("Collection Timer started")
93 : logging::logMessage("Collection Timer re-started");
94
95 l_timer.async_wait([this](const boost::system::error_code& ec) {
96 if (ec == boost::asio::error::operation_aborted)
97 {
98 throw std::runtime_error(
99 "Timer to detect thread collection status was aborted");
100 }
101
102 if (ec)
103 {
104 throw std::runtime_error(
105 "Timer to detect thread collection failed");
106 }
107
108 if (m_worker->isAllFruCollectionDone())
109 {
110 // cancel the timer
111 l_timer.cancel();
112 processFailedEeproms();
113
114 // update VPD for powerVS system.
115 ConfigurePowerVsSystem();
116
117 std::cout << "m_worker->isSystemVPDOnDBus() completed" << std::endl;
Anupama B Rda9806b2025-08-29 02:41:10 -0500118 m_progressInterface->set_property(
119 "Status", std::string(constants::vpdCollectionCompleted));
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530120
121 if (m_backupAndRestoreObj)
122 {
123 m_backupAndRestoreObj->backupAndRestore();
124 }
Souvik Roy3d4af4a2025-06-23 04:45:27 -0500125
126 if (m_eventListener)
127 {
128 m_eventListener->registerCorrPropCallBack();
129 }
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530130 }
131 else
132 {
133 auto l_threadCount = m_worker->getActiveThreadCount();
134 if (l_timerRetry == MAX_RETRY)
135 {
136 l_timer.cancel();
137 logging::logMessage("Taking too long. Active thread = " +
138 std::to_string(l_threadCount));
139 }
140 else
141 {
142 l_timerRetry++;
143 logging::logMessage("Collection is in progress for [" +
144 std::to_string(l_threadCount) + "] FRUs.");
145
146 SetTimerToDetectVpdCollectionStatus();
147 }
148 }
149 });
150}
151
152void IbmHandler::checkAndUpdatePowerVsVpd(
153 const nlohmann::json& i_powerVsJsonObj,
154 std::vector<std::string>& o_failedPathList)
155{
156 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
157 {
158 nlohmann::json l_sysCfgJsonObj{};
159 if (m_worker.get() != nullptr)
160 {
161 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
162 }
163
164 // The utility method will handle emty JSON case. No explicit
165 // handling required here.
Rekha Aparna017567a2025-08-13 02:07:06 -0500166 uint16_t l_errCode = 0;
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530167 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
Rekha Aparna017567a2025-08-13 02:07:06 -0500168 l_sysCfgJsonObj, l_fruPath, l_errCode);
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530169
170 // Mark it as failed if inventory path not found in JSON.
171 if (l_inventoryPath.empty())
172 {
Rekha Aparna017567a2025-08-13 02:07:06 -0500173 if (l_errCode)
174 {
175 logging::logMessage(
176 "Failed to get inventory object path from JSON for FRU [" +
177 l_fruPath + "], error : " +
178 vpdSpecificUtility::getErrCodeMsg(l_errCode));
179 }
180
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530181 o_failedPathList.push_back(l_fruPath);
182 continue;
183 }
184
185 // check if the FRU is present
186 if (!dbusUtility::isInventoryPresent(l_inventoryPath))
187 {
188 logging::logMessage(
189 "Inventory not present, skip updating part number. Path: " +
190 l_inventoryPath);
191 continue;
192 }
193
194 // check if the FRU needs CCIN check before updating PN.
195 if (l_recJson.contains("CCIN"))
196 {
197 const auto& l_ccinFromDbus =
198 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath);
199
200 // Not an ideal situation as CCIN can't be empty.
201 if (l_ccinFromDbus.empty())
202 {
203 o_failedPathList.push_back(l_fruPath);
204 continue;
205 }
206
207 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
208
209 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
210 l_ccinFromDbus) == l_ccinListFromJson.end())
211 {
212 // Don't update PN in this case.
213 continue;
214 }
215 }
216
217 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
218 {
219 // Record name can't be CCIN, skip processing as it is there for PN
220 // update based on CCIN check.
221 if (l_recordName == constants::kwdCCIN)
222 {
223 continue;
224 }
225
226 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
227 {
228 // Is value of type array.
229 if (!l_kwdValue.is_array())
230 {
231 o_failedPathList.push_back(l_fruPath);
232 continue;
233 }
234
235 // Get current FRU Part number.
236 auto l_retVal = dbusUtility::readDbusProperty(
237 constants::pimServiceName, l_inventoryPath,
238 constants::viniInf, constants::kwdFN);
239
240 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
241
242 if (!l_ptrToFn)
243 {
244 o_failedPathList.push_back(l_fruPath);
245 continue;
246 }
247
248 types::BinaryVector l_binaryKwdValue =
249 l_kwdValue.get<types::BinaryVector>();
250 if (l_binaryKwdValue == (*l_ptrToFn))
251 {
252 continue;
253 }
254
255 // Update part number only if required.
256 std::shared_ptr<Parser> l_parserObj =
257 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
258 if (l_parserObj->updateVpdKeyword(std::make_tuple(
259 l_recordName, l_kwdName, l_binaryKwdValue)) ==
260 constants::FAILURE)
261 {
262 o_failedPathList.push_back(l_fruPath);
263 continue;
264 }
265
266 // update the Asset interface Spare part number explicitly.
267 if (!dbusUtility::callPIM(types::ObjectMap{
268 {l_inventoryPath,
269 {{constants::assetInf,
270 {{"SparePartNumber",
271 std::string(l_binaryKwdValue.begin(),
272 l_binaryKwdValue.end())}}}}}}))
273 {
274 logging::logMessage(
275 "Updating Spare Part Number under Asset interface failed for path [" +
276 l_inventoryPath + "]");
277 }
278
279 // Just needed for logging.
280 std::string l_initialPartNum((*l_ptrToFn).begin(),
281 (*l_ptrToFn).end());
282 std::string l_finalPartNum(l_binaryKwdValue.begin(),
283 l_binaryKwdValue.end());
284 logging::logMessage(
285 "FRU Part number updated for path [" + l_inventoryPath +
286 "]" + "From [" + l_initialPartNum + "]" + " to [" +
287 l_finalPartNum + "]");
288 }
289 }
290 }
291}
292
293void IbmHandler::ConfigurePowerVsSystem()
294{
295 std::vector<std::string> l_failedPathList;
296 try
297 {
298 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
299 if (l_imValue.empty())
300 {
301 throw DbusException("Invalid IM value read from Dbus");
302 }
303
304 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
305 {
306 // TODO: Should booting be blocked in case of some
307 // misconfigurations?
308 return;
309 }
310
Rekha Aparnaadf85262025-09-09 00:41:00 -0500311 uint16_t l_errCode = 0;
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530312 const nlohmann::json& l_powerVsJsonObj =
Rekha Aparnaadf85262025-09-09 00:41:00 -0500313 jsonUtility::getPowerVsJson(l_imValue, l_errCode);
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530314
315 if (l_powerVsJsonObj.empty())
316 {
Rekha Aparnaadf85262025-09-09 00:41:00 -0500317 throw std::runtime_error(
318 "PowerVS Json not found. Error : " +
319 vpdSpecificUtility::getErrCodeMsg(l_errCode));
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530320 }
321
322 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
323
324 if (!l_failedPathList.empty())
325 {
326 throw std::runtime_error(
327 "Part number update failed for following paths: ");
328 }
329 }
330 catch (const std::exception& l_ex)
331 {
332 // TODO log appropriate PEL
333 }
334}
335
336void IbmHandler::processFailedEeproms()
337{
338 if (m_worker.get() != nullptr)
339 {
340 // TODO:
341 // - iterate through list of EEPROMs for which thread creation has
342 // failed
343 // - For each failed EEPROM, trigger VPD collection
344 m_worker->getFailedEepromPaths().clear();
345 }
346}
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530347
Sunny Srivastava78a50422025-04-25 11:17:56 +0530348void IbmHandler::primeSystemBlueprint()
349{
350 if (m_sysCfgJsonObj.empty())
351 {
352 return;
353 }
354
355 const nlohmann::json& l_listOfFrus =
356 m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
357
358 for (const auto& l_itemFRUS : l_listOfFrus.items())
359 {
360 const std::string& l_vpdFilePath = l_itemFRUS.key();
361
362 if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH)
363 {
364 continue;
365 }
366
367 // Prime the inventry for FRUs which
368 // are not present/processing had some error.
369 if (m_worker.get() != nullptr &&
370 !m_worker->primeInventory(l_vpdFilePath))
371 {
372 logging::logMessage(
373 "Priming of inventory failed for FRU " + l_vpdFilePath);
374 }
375 }
376}
377
378void IbmHandler::enableMuxChips()
379{
380 if (m_sysCfgJsonObj.empty())
381 {
382 // config JSON should not be empty at this point of execution.
383 throw std::runtime_error("Config JSON is empty. Can't enable muxes");
384 return;
385 }
386
387 if (!m_sysCfgJsonObj.contains("muxes"))
388 {
389 logging::logMessage("No mux defined for the system in config JSON");
390 return;
391 }
392
393 // iterate over each MUX detail and enable them.
394 for (const auto& item : m_sysCfgJsonObj["muxes"])
395 {
396 if (item.contains("holdidlepath"))
397 {
398 std::string cmd = "echo 0 > ";
399 cmd += item["holdidlepath"];
400
401 logging::logMessage("Enabling mux with command = " + cmd);
402
403 commonUtility::executeCmd(cmd);
404 continue;
405 }
406
407 logging::logMessage(
408 "Mux Entry does not have hold idle path. Can't enable the mux");
409 }
410}
411
412void IbmHandler::performInitialSetup()
413{
414 try
415 {
Anupama B R281e2d42025-05-05 10:05:13 -0500416 if (m_worker.get() == nullptr)
417 {
418 throw std::runtime_error(
419 "Worker object not found. Can't perform initial setup.");
420 }
421
422 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
Sunny Srivastava78a50422025-04-25 11:17:56 +0530423 if (!dbusUtility::isChassisPowerOn())
424 {
Anupama B R281e2d42025-05-05 10:05:13 -0500425 m_worker->setDeviceTreeAndJson();
426
427 // Since the above function setDeviceTreeAndJson can change the json
428 // which is used, we would need to reacquire the json object again
429 // here.
Sunny Srivastava78a50422025-04-25 11:17:56 +0530430 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
Anupama B R281e2d42025-05-05 10:05:13 -0500431
Anupama B Rec7f8862025-05-23 01:21:01 -0500432 if (isPrimingRequired())
433 {
434 primeSystemBlueprint();
435 }
Sunny Srivastava78a50422025-04-25 11:17:56 +0530436 }
437
438 // Enable all mux which are used for connecting to the i2c on the
439 // pcie slots for pcie cards. These are not enabled by kernel due to
440 // an issue seen with Castello cards, where the i2c line hangs on a
441 // probe.
442 enableMuxChips();
443
444 // Nothing needs to be done. Service restarted or BMC re-booted for
445 // some reason at system power on.
Sunny Srivastava78a50422025-04-25 11:17:56 +0530446 }
447 catch (const std::exception& l_ex)
448 {
Anupama B R4c65fcd2025-09-01 08:09:00 -0500449 m_worker->setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
450 constants::vpdCollectionFailed);
Sunny Srivastava78a50422025-04-25 11:17:56 +0530451 // Any issue in system's inital set up is handled in this catch. Error
452 // will not propogate to manager.
453 EventLogger::createSyncPel(
454 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
455 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
456 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
457 }
458}
459
Anupama B Rec7f8862025-05-23 01:21:01 -0500460bool IbmHandler::isPrimingRequired() const noexcept
461{
462 try
463 {
464 // get all object paths under PIM
465 const auto l_objectPaths = dbusUtility::GetSubTreePaths(
466 constants::systemInvPath, 0,
467 std::vector<std::string>{constants::vpdCollectionInterface});
468
469 const nlohmann::json& l_listOfFrus =
470 m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
471
472 size_t l_invPathCount = 0;
473
474 for (const auto& l_itemFRUS : l_listOfFrus.items())
475 {
476 for (const auto& l_Fru : l_itemFRUS.value())
477 {
478 if (l_Fru.contains("ccin") || (l_Fru.contains("noprime") &&
479 l_Fru.value("noprime", false)))
480 {
481 continue;
482 }
483
484 l_invPathCount += 1;
485 }
486 }
487 return ((l_objectPaths.size() >= l_invPathCount) ? false : true);
488 }
489 catch (const std::exception& l_ex)
490 {
491 logging::logMessage(
492 "Error while checking is priming required or not, error: " +
493 std::string(l_ex.what()));
494 }
495
496 // In case of any error, perform priming, as it's unclear whether priming is
497 // required.
498 return true;
499}
Anupama B R7127ab42025-06-26 01:39:08 -0500500
501void IbmHandler::collectAllFruVpd()
502{
503 // Setting status to "InProgress", before trigeering VPD collection.
Anupama B Rda9806b2025-08-29 02:41:10 -0500504 m_progressInterface->set_property(
505 "Status", std::string(constants::vpdCollectionInProgress));
Anupama B R7127ab42025-06-26 01:39:08 -0500506 m_worker->collectFrusFromJson();
507 SetTimerToDetectVpdCollectionStatus();
508}
Sunny Srivastava867ee752025-04-15 12:24:23 +0530509} // namespace vpd