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