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