blob: 1c39acca814d90285cd37d1b92a9f23c148810df [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
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +05305#include "parser.hpp"
6
7#include <utility/dbus_utility.hpp>
8#include <utility/json_utility.hpp>
9#include <utility/vpd_specific_utility.hpp>
10
Sunny Srivastava867ee752025-04-15 12:24:23 +053011namespace vpd
12{
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053013IbmHandler::IbmHandler(
14 std::shared_ptr<Worker>& o_worker,
15 std::shared_ptr<BackupAndRestore>& o_backupAndRestoreObj,
16 const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_iFace,
17 const std::shared_ptr<boost::asio::io_context>& i_ioCon,
18 const std::shared_ptr<sdbusplus::asio::connection>& i_asioConnection) :
19 m_worker(o_worker), m_backupAndRestoreObj(o_backupAndRestoreObj),
20 m_interface(i_iFace), m_ioContext(i_ioCon),
21 m_asioConnection(i_asioConnection)
22{
23 if (dbusUtility::isChassisPowerOn())
24 {
25 // At power on, less number of FRU(s) needs collection. we can scale
26 // down the threads to reduce CPU utilization.
27 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT,
28 constants::VALUE_1);
29 }
30 else
31 {
32 // Initialize with default configuration
33 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
34 }
35
36 // Set up minimal things that is needed before bus name is claimed.
37 m_worker->performInitialSetup();
38
39 // Get the system config JSON object
40 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
41
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
Sunny Srivastava380efbb2025-04-25 10:28:30 +053062 // callback to detect host state change.
63 registerHostStateChangeCallback();
64
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +053065 // set callback to detect any asset tag change
66 registerAssetTagChangeCallback();
67
68 // set async timer to detect if system VPD is published on D-Bus.
69 SetTimerToDetectSVPDOnDbus();
70
71 // set async timer to detect if VPD collection is done.
72 SetTimerToDetectVpdCollectionStatus();
73
74 // Instantiate GpioMonitor class
75 m_gpioMonitor =
76 std::make_shared<GpioMonitor>(m_sysCfgJsonObj, m_worker, m_ioContext);
77}
78
79void IbmHandler::registerAssetTagChangeCallback()
80{
81 static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
82 std::make_shared<sdbusplus::bus::match_t>(
83 *m_asioConnection,
84 sdbusplus::bus::match::rules::propertiesChanged(
85 constants::systemInvPath, constants::assetTagInf),
86 [this](sdbusplus::message_t& l_msg) {
87 processAssetTagChangeCallback(l_msg);
88 });
89}
90
91void IbmHandler::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
92{
93 try
94 {
95 if (i_msg.is_method_error())
96 {
97 throw std::runtime_error(
98 "Error reading callback msg for asset tag.");
99 }
100
101 std::string l_objectPath;
102 types::PropertyMap l_propMap;
103 i_msg.read(l_objectPath, l_propMap);
104
105 const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
106 if (l_itrToAssetTag != l_propMap.end())
107 {
108 if (auto l_assetTag =
109 std::get_if<std::string>(&(l_itrToAssetTag->second)))
110 {
111 // Call Notify to persist the AssetTag
112 types::ObjectMap l_objectMap = {
113 {sdbusplus::message::object_path(constants::systemInvPath),
114 {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
115
116 // Notify PIM
117 if (!dbusUtility::callPIM(move(l_objectMap)))
118 {
119 throw std::runtime_error(
120 "Call to PIM failed for asset tag update.");
121 }
122 }
123 }
124 else
125 {
126 throw std::runtime_error(
127 "Could not find asset tag in callback message.");
128 }
129 }
130 catch (const std::exception& l_ex)
131 {
132 // TODO: Log PEL with below description.
133 logging::logMessage("Asset tag callback update failed with error: " +
134 std::string(l_ex.what()));
135 }
136}
137
138void IbmHandler::SetTimerToDetectSVPDOnDbus()
139{
140 try
141 {
142 static boost::asio::steady_timer timer(*m_ioContext);
143
144 // timer for 2 seconds
145 auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
146
147 (asyncCancelled == 0) ? logging::logMessage("Timer started")
148 : logging::logMessage("Timer re-started");
149
150 timer.async_wait([this](const boost::system::error_code& ec) {
151 if (ec == boost::asio::error::operation_aborted)
152 {
153 throw std::runtime_error(
154 std::string(__FUNCTION__) +
155 ": Timer to detect system VPD collection status was aborted.");
156 }
157
158 if (ec)
159 {
160 throw std::runtime_error(
161 std::string(__FUNCTION__) +
162 ": Timer to detect System VPD collection failed");
163 }
164
165 if (m_worker->isSystemVPDOnDBus())
166 {
167 // cancel the timer
168 timer.cancel();
169
170 // Triggering FRU VPD collection. Setting status to "In
171 // Progress".
172 m_interface->set_property("CollectionStatus",
173 std::string("InProgress"));
174 m_worker->collectFrusFromJson();
175 }
176 });
177 }
178 catch (const std::exception& l_ex)
179 {
180 EventLogger::createAsyncPel(
181 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
182 __FILE__, __FUNCTION__, 0,
183 std::string("Collection for FRUs failed with reason:") +
184 EventLogger::getErrorMsg(l_ex),
185 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
186 }
187}
188
189void IbmHandler::SetTimerToDetectVpdCollectionStatus()
190{
191 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
192 // system type.
193 static constexpr auto MAX_RETRY = 12;
194
195 static boost::asio::steady_timer l_timer(*m_ioContext);
196 static uint8_t l_timerRetry = 0;
197
198 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
199
200 (l_asyncCancelled == 0)
201 ? logging::logMessage("Collection Timer started")
202 : logging::logMessage("Collection Timer re-started");
203
204 l_timer.async_wait([this](const boost::system::error_code& ec) {
205 if (ec == boost::asio::error::operation_aborted)
206 {
207 throw std::runtime_error(
208 "Timer to detect thread collection status was aborted");
209 }
210
211 if (ec)
212 {
213 throw std::runtime_error(
214 "Timer to detect thread collection failed");
215 }
216
217 if (m_worker->isAllFruCollectionDone())
218 {
219 // cancel the timer
220 l_timer.cancel();
221 processFailedEeproms();
222
223 // update VPD for powerVS system.
224 ConfigurePowerVsSystem();
225
226 std::cout << "m_worker->isSystemVPDOnDBus() completed" << std::endl;
227 m_interface->set_property("CollectionStatus",
228 std::string("Completed"));
229
230 if (m_backupAndRestoreObj)
231 {
232 m_backupAndRestoreObj->backupAndRestore();
233 }
234 }
235 else
236 {
237 auto l_threadCount = m_worker->getActiveThreadCount();
238 if (l_timerRetry == MAX_RETRY)
239 {
240 l_timer.cancel();
241 logging::logMessage("Taking too long. Active thread = " +
242 std::to_string(l_threadCount));
243 }
244 else
245 {
246 l_timerRetry++;
247 logging::logMessage("Collection is in progress for [" +
248 std::to_string(l_threadCount) + "] FRUs.");
249
250 SetTimerToDetectVpdCollectionStatus();
251 }
252 }
253 });
254}
255
256void IbmHandler::checkAndUpdatePowerVsVpd(
257 const nlohmann::json& i_powerVsJsonObj,
258 std::vector<std::string>& o_failedPathList)
259{
260 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
261 {
262 nlohmann::json l_sysCfgJsonObj{};
263 if (m_worker.get() != nullptr)
264 {
265 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
266 }
267
268 // The utility method will handle emty JSON case. No explicit
269 // handling required here.
270 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
271 l_sysCfgJsonObj, l_fruPath);
272
273 // Mark it as failed if inventory path not found in JSON.
274 if (l_inventoryPath.empty())
275 {
276 o_failedPathList.push_back(l_fruPath);
277 continue;
278 }
279
280 // check if the FRU is present
281 if (!dbusUtility::isInventoryPresent(l_inventoryPath))
282 {
283 logging::logMessage(
284 "Inventory not present, skip updating part number. Path: " +
285 l_inventoryPath);
286 continue;
287 }
288
289 // check if the FRU needs CCIN check before updating PN.
290 if (l_recJson.contains("CCIN"))
291 {
292 const auto& l_ccinFromDbus =
293 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath);
294
295 // Not an ideal situation as CCIN can't be empty.
296 if (l_ccinFromDbus.empty())
297 {
298 o_failedPathList.push_back(l_fruPath);
299 continue;
300 }
301
302 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
303
304 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
305 l_ccinFromDbus) == l_ccinListFromJson.end())
306 {
307 // Don't update PN in this case.
308 continue;
309 }
310 }
311
312 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
313 {
314 // Record name can't be CCIN, skip processing as it is there for PN
315 // update based on CCIN check.
316 if (l_recordName == constants::kwdCCIN)
317 {
318 continue;
319 }
320
321 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
322 {
323 // Is value of type array.
324 if (!l_kwdValue.is_array())
325 {
326 o_failedPathList.push_back(l_fruPath);
327 continue;
328 }
329
330 // Get current FRU Part number.
331 auto l_retVal = dbusUtility::readDbusProperty(
332 constants::pimServiceName, l_inventoryPath,
333 constants::viniInf, constants::kwdFN);
334
335 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
336
337 if (!l_ptrToFn)
338 {
339 o_failedPathList.push_back(l_fruPath);
340 continue;
341 }
342
343 types::BinaryVector l_binaryKwdValue =
344 l_kwdValue.get<types::BinaryVector>();
345 if (l_binaryKwdValue == (*l_ptrToFn))
346 {
347 continue;
348 }
349
350 // Update part number only if required.
351 std::shared_ptr<Parser> l_parserObj =
352 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
353 if (l_parserObj->updateVpdKeyword(std::make_tuple(
354 l_recordName, l_kwdName, l_binaryKwdValue)) ==
355 constants::FAILURE)
356 {
357 o_failedPathList.push_back(l_fruPath);
358 continue;
359 }
360
361 // update the Asset interface Spare part number explicitly.
362 if (!dbusUtility::callPIM(types::ObjectMap{
363 {l_inventoryPath,
364 {{constants::assetInf,
365 {{"SparePartNumber",
366 std::string(l_binaryKwdValue.begin(),
367 l_binaryKwdValue.end())}}}}}}))
368 {
369 logging::logMessage(
370 "Updating Spare Part Number under Asset interface failed for path [" +
371 l_inventoryPath + "]");
372 }
373
374 // Just needed for logging.
375 std::string l_initialPartNum((*l_ptrToFn).begin(),
376 (*l_ptrToFn).end());
377 std::string l_finalPartNum(l_binaryKwdValue.begin(),
378 l_binaryKwdValue.end());
379 logging::logMessage(
380 "FRU Part number updated for path [" + l_inventoryPath +
381 "]" + "From [" + l_initialPartNum + "]" + " to [" +
382 l_finalPartNum + "]");
383 }
384 }
385 }
386}
387
388void IbmHandler::ConfigurePowerVsSystem()
389{
390 std::vector<std::string> l_failedPathList;
391 try
392 {
393 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
394 if (l_imValue.empty())
395 {
396 throw DbusException("Invalid IM value read from Dbus");
397 }
398
399 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
400 {
401 // TODO: Should booting be blocked in case of some
402 // misconfigurations?
403 return;
404 }
405
406 const nlohmann::json& l_powerVsJsonObj =
407 jsonUtility::getPowerVsJson(l_imValue);
408
409 if (l_powerVsJsonObj.empty())
410 {
411 throw std::runtime_error("PowerVS Json not found");
412 }
413
414 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
415
416 if (!l_failedPathList.empty())
417 {
418 throw std::runtime_error(
419 "Part number update failed for following paths: ");
420 }
421 }
422 catch (const std::exception& l_ex)
423 {
424 // TODO log appropriate PEL
425 }
426}
427
428void IbmHandler::processFailedEeproms()
429{
430 if (m_worker.get() != nullptr)
431 {
432 // TODO:
433 // - iterate through list of EEPROMs for which thread creation has
434 // failed
435 // - For each failed EEPROM, trigger VPD collection
436 m_worker->getFailedEepromPaths().clear();
437 }
438}
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530439
440void IbmHandler::registerHostStateChangeCallback()
441{
442 static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
443 std::make_shared<sdbusplus::bus::match_t>(
444 *m_asioConnection,
445 sdbusplus::bus::match::rules::propertiesChanged(
446 constants::hostObjectPath, constants::hostInterface),
447 [this](sdbusplus::message_t& i_msg) {
448 hostStateChangeCallBack(i_msg);
449 });
450}
451
452void IbmHandler::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
453{
454 try
455 {
456 if (i_msg.is_method_error())
457 {
458 throw std::runtime_error(
459 "Error reading callback message for host state");
460 }
461
462 std::string l_objectPath;
463 types::PropertyMap l_propMap;
464 i_msg.read(l_objectPath, l_propMap);
465
466 const auto l_itr = l_propMap.find("CurrentHostState");
467
468 if (l_itr == l_propMap.end())
469 {
470 throw std::runtime_error(
471 "CurrentHostState field is missing in callback message");
472 }
473
474 if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
475 {
476 // implies system is moving from standby to power on state
477 if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
478 "TransitioningToRunning")
479 {
480 // TODO: check for all the essential FRUs in the system.
481
482 if (m_worker.get() != nullptr)
483 {
484 // Perform recollection.
485 m_worker->performVpdRecollection();
486 }
487 else
488 {
489 logging::logMessage(
490 "Failed to get worker object, Abort re-collection");
491 }
492 }
493 }
494 else
495 {
496 throw std::runtime_error(
497 "Invalid type recieved in variant for host state.");
498 }
499 }
500 catch (const std::exception& l_ex)
501 {
502 // TODO: Log PEL.
503 logging::logMessage(l_ex.what());
504 }
505}
Sunny Srivastava867ee752025-04-15 12:24:23 +0530506} // namespace vpd