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