blob: fd63ffcf32fa485c55efa4d255bad177dde6fe05 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#include "config.h"
2
3#include "manager.hpp"
4
5#include "backup_restore.hpp"
6#include "constants.hpp"
7#include "exceptions.hpp"
8#include "logger.hpp"
9#include "parser.hpp"
10#include "parser_factory.hpp"
11#include "parser_interface.hpp"
12#include "types.hpp"
13#include "utility/dbus_utility.hpp"
14#include "utility/json_utility.hpp"
15#include "utility/vpd_specific_utility.hpp"
16
17#include <boost/asio/steady_timer.hpp>
18#include <sdbusplus/bus/match.hpp>
19#include <sdbusplus/message.hpp>
20
21namespace vpd
22{
23Manager::Manager(
24 const std::shared_ptr<boost::asio::io_context>& ioCon,
25 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
26 const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
27 m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection)
28{
29 try
30 {
31#ifdef IBM_SYSTEM
Sunny Srivastava765cf7b2025-02-04 05:24:11 -060032 if (dbusUtility::isChassisPowerOn())
33 {
34 // At power on, less number of FRU(s) needs collection. we can scale
35 // down the threads to reduce CPU utilization.
36 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT,
37 constants::VALUE_1);
38 }
39 else
40 {
41 // Initialize with default configuration
42 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
43 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050044
45 // Set up minimal things that is needed before bus name is claimed.
46 m_worker->performInitialSetup();
47
48 // set callback to detect any asset tag change
49 registerAssetTagChangeCallback();
50
51 // set async timer to detect if system VPD is published on D-Bus.
52 SetTimerToDetectSVPDOnDbus();
53
54 // set async timer to detect if VPD collection is done.
55 SetTimerToDetectVpdCollectionStatus();
56
57 // Instantiate GpioMonitor class
58 m_gpioMonitor = std::make_shared<GpioMonitor>(
59 m_worker->getSysCfgJsonObj(), m_worker, m_ioContext);
60
61#endif
62 // set callback to detect host state change.
63 registerHostStateChangeCallback();
64
65 // For backward compatibility. Should be depricated.
66 iFace->register_method(
67 "WriteKeyword",
68 [this](const sdbusplus::message::object_path i_path,
69 const std::string i_recordName, const std::string i_keyword,
70 const types::BinaryVector i_value) -> int {
71 return this->updateKeyword(
72 i_path, std::make_tuple(i_recordName, i_keyword, i_value));
73 });
74
75 // Register methods under com.ibm.VPD.Manager interface
76 iFace->register_method(
77 "UpdateKeyword",
78 [this](const types::Path i_vpdPath,
79 const types::WriteVpdParams i_paramsToWriteData) -> int {
80 return this->updateKeyword(i_vpdPath, i_paramsToWriteData);
81 });
82
83 iFace->register_method(
84 "WriteKeywordOnHardware",
85 [this](const types::Path i_fruPath,
86 const types::WriteVpdParams i_paramsToWriteData) -> int {
87 return this->updateKeywordOnHardware(i_fruPath,
88 i_paramsToWriteData);
89 });
90
91 iFace->register_method(
92 "ReadKeyword",
93 [this](const types::Path i_fruPath,
94 const types::ReadVpdParams i_paramsToReadData)
95 -> types::DbusVariantType {
96 return this->readKeyword(i_fruPath, i_paramsToReadData);
97 });
98
99 iFace->register_method(
100 "CollectFRUVPD",
101 [this](const sdbusplus::message::object_path& i_dbusObjPath) {
102 this->collectSingleFruVpd(i_dbusObjPath);
103 });
104
105 iFace->register_method(
106 "deleteFRUVPD",
107 [this](const sdbusplus::message::object_path& i_dbusObjPath) {
108 this->deleteSingleFruVpd(i_dbusObjPath);
109 });
110
111 iFace->register_method(
112 "GetExpandedLocationCode",
113 [this](const std::string& i_unexpandedLocationCode,
114 uint16_t& i_nodeNumber) -> std::string {
115 return this->getExpandedLocationCode(i_unexpandedLocationCode,
116 i_nodeNumber);
117 });
118
119 iFace->register_method("GetFRUsByExpandedLocationCode",
120 [this](const std::string& i_expandedLocationCode)
121 -> types::ListOfPaths {
122 return this->getFrusByExpandedLocationCode(
123 i_expandedLocationCode);
124 });
125
126 iFace->register_method(
127 "GetFRUsByUnexpandedLocationCode",
128 [this](const std::string& i_unexpandedLocationCode,
129 uint16_t& i_nodeNumber) -> types::ListOfPaths {
130 return this->getFrusByUnexpandedLocationCode(
131 i_unexpandedLocationCode, i_nodeNumber);
132 });
133
134 iFace->register_method(
135 "GetHardwarePath",
136 [this](const sdbusplus::message::object_path& i_dbusObjPath)
137 -> std::string { return this->getHwPath(i_dbusObjPath); });
138
139 iFace->register_method("PerformVPDRecollection", [this]() {
140 this->performVpdRecollection();
141 });
142
143 // Indicates FRU VPD collection for the system has not started.
144 iFace->register_property_rw<std::string>(
145 "CollectionStatus", sdbusplus::vtable::property_::emits_change,
146 [this](const std::string l_currStatus, const auto&) {
147 m_vpdCollectionStatus = l_currStatus;
148 return 0;
149 },
150 [this](const auto&) { return m_vpdCollectionStatus; });
151 }
152 catch (const std::exception& e)
153 {
154 logging::logMessage(
155 "VPD-Manager service failed. " + std::string(e.what()));
156 throw;
157 }
158}
159
160#ifdef IBM_SYSTEM
161void Manager::registerAssetTagChangeCallback()
162{
163 static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
164 std::make_shared<sdbusplus::bus::match_t>(
165 *m_asioConnection,
166 sdbusplus::bus::match::rules::propertiesChanged(
167 constants::systemInvPath, constants::assetTagInf),
168 [this](sdbusplus::message_t& l_msg) {
169 processAssetTagChangeCallback(l_msg);
170 });
171}
172
173void Manager::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
174{
175 try
176 {
177 if (i_msg.is_method_error())
178 {
179 throw std::runtime_error(
180 "Error reading callback msg for asset tag.");
181 }
182
183 std::string l_objectPath;
184 types::PropertyMap l_propMap;
185 i_msg.read(l_objectPath, l_propMap);
186
187 const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
188 if (l_itrToAssetTag != l_propMap.end())
189 {
190 if (auto l_assetTag =
191 std::get_if<std::string>(&(l_itrToAssetTag->second)))
192 {
193 // Call Notify to persist the AssetTag
194 types::ObjectMap l_objectMap = {
195 {sdbusplus::message::object_path(constants::systemInvPath),
196 {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
197
198 // Notify PIM
199 if (!dbusUtility::callPIM(move(l_objectMap)))
200 {
201 throw std::runtime_error(
202 "Call to PIM failed for asset tag update.");
203 }
204 }
205 }
206 else
207 {
208 throw std::runtime_error(
209 "Could not find asset tag in callback message.");
210 }
211 }
212 catch (const std::exception& l_ex)
213 {
214 // TODO: Log PEL with below description.
215 logging::logMessage("Asset tag callback update failed with error: " +
216 std::string(l_ex.what()));
217 }
218}
219
220void Manager::SetTimerToDetectSVPDOnDbus()
221{
222 static boost::asio::steady_timer timer(*m_ioContext);
223
224 // timer for 2 seconds
225 auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
226
227 (asyncCancelled == 0) ? logging::logMessage("Timer started")
228 : logging::logMessage("Timer re-started");
229
230 timer.async_wait([this](const boost::system::error_code& ec) {
231 if (ec == boost::asio::error::operation_aborted)
232 {
233 throw std::runtime_error(
234 "Timer to detect system VPD collection status was aborted");
235 }
236
237 if (ec)
238 {
239 throw std::runtime_error(
240 "Timer to detect System VPD collection failed");
241 }
242
243 if (m_worker->isSystemVPDOnDBus())
244 {
245 // cancel the timer
246 timer.cancel();
247
248 // Triggering FRU VPD collection. Setting status to "In
249 // Progress".
250 m_interface->set_property("CollectionStatus",
251 std::string("InProgress"));
252 m_worker->collectFrusFromJson();
253 }
254 });
255}
256
257void Manager::SetTimerToDetectVpdCollectionStatus()
258{
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600259 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500260 // system type.
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600261 static constexpr auto MAX_RETRY = 12;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500262
263 static boost::asio::steady_timer l_timer(*m_ioContext);
264 static uint8_t l_timerRetry = 0;
265
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600266 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500267
268 (l_asyncCancelled == 0)
269 ? logging::logMessage("Collection Timer started")
270 : logging::logMessage("Collection Timer re-started");
271
272 l_timer.async_wait([this](const boost::system::error_code& ec) {
273 if (ec == boost::asio::error::operation_aborted)
274 {
275 throw std::runtime_error(
276 "Timer to detect thread collection status was aborted");
277 }
278
279 if (ec)
280 {
281 throw std::runtime_error(
282 "Timer to detect thread collection failed");
283 }
284
285 if (m_worker->isAllFruCollectionDone())
286 {
287 // cancel the timer
288 l_timer.cancel();
Souvik Roy1f4c8f82025-01-23 00:37:43 -0600289 processFailedEeproms();
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530290
291 // update VPD for powerVS system.
292 ConfigurePowerVsSystem();
293
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500294 m_interface->set_property("CollectionStatus",
295 std::string("Completed"));
296
297 const nlohmann::json& l_sysCfgJsonObj =
298 m_worker->getSysCfgJsonObj();
299 if (jsonUtility::isBackupAndRestoreRequired(l_sysCfgJsonObj))
300 {
301 BackupAndRestore l_backupAndRestoreObj(l_sysCfgJsonObj);
302 l_backupAndRestoreObj.backupAndRestore();
303 }
304 }
305 else
306 {
307 auto l_threadCount = m_worker->getActiveThreadCount();
308 if (l_timerRetry == MAX_RETRY)
309 {
310 l_timer.cancel();
311 logging::logMessage("Taking too long. Active thread = " +
312 std::to_string(l_threadCount));
313 }
314 else
315 {
316 l_timerRetry++;
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600317 logging::logMessage("Collection is in progress for [" +
318 std::to_string(l_threadCount) + "] FRUs.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500319
320 SetTimerToDetectVpdCollectionStatus();
321 }
322 }
323 });
324}
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530325
Sunny Srivastava022112b2025-02-19 19:53:29 +0530326void Manager::checkAndUpdatePowerVsVpd(
327 const nlohmann::json& i_powerVsJsonObj,
328 std::vector<std::string>& o_failedPathList)
329{
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530330 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
331 {
332 // TODO add special handling for PROC CCIN check.
333 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
334 {
335 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
336 {
337 // Is value of type array.
338 if (!l_kwdValue.is_array())
339 {
340 o_failedPathList.push_back(l_fruPath);
341 continue;
342 }
343
344 nlohmann::json l_sysCfgJsonObj{};
345 if (m_worker.get() != nullptr)
346 {
347 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
348 }
349
350 // The utility method will handle empty JSON case. No explicit
351 // handling required here.
352 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
353 l_sysCfgJsonObj, l_fruPath);
354
355 // Mark it as failed if inventory path not found in JSON.
356 if (l_inventoryPath.empty())
357 {
358 o_failedPathList.push_back(l_fruPath);
359 continue;
360 }
361
362 // Get current Part number.
363 auto l_retVal = dbusUtility::readDbusProperty(
364 constants::pimServiceName, l_inventoryPath,
365 constants::viniInf, constants::kwdPN);
366
367 auto l_ptrToPn = std::get_if<types::BinaryVector>(&l_retVal);
368
369 if (!l_ptrToPn)
370 {
371 o_failedPathList.push_back(l_fruPath);
372 continue;
373 }
374
375 types::BinaryVector l_binaryKwdValue =
376 l_kwdValue.get<types::BinaryVector>();
377 if (l_binaryKwdValue == (*l_ptrToPn))
378 {
379 continue;
380 }
381
382 // Update part number only if required.
383 if (updateKeyword(
384 l_fruPath,
385 std::make_tuple(l_recordName, l_kwdName, l_kwdValue)) ==
386 constants::FAILURE)
387 {
388 o_failedPathList.push_back(l_fruPath);
389 continue;
390 }
391
392 // Just needed for logging.
393 std::string l_initialPartNum((*l_ptrToPn).begin(),
394 (*l_ptrToPn).end());
395 std::string l_finalPartNum(l_binaryKwdValue.begin(),
396 l_binaryKwdValue.end());
397 logging::logMessage(
398 "Part number updated for path [" + l_inventoryPath + "]" +
399 "From [" + l_initialPartNum + "]" + " to [" +
400 l_finalPartNum + "]");
401 }
402 }
403 }
Sunny Srivastava022112b2025-02-19 19:53:29 +0530404}
405
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530406void Manager::ConfigurePowerVsSystem()
407{
Sunny Srivastava022112b2025-02-19 19:53:29 +0530408 std::vector<std::string> l_failedPathList;
Sunny Srivastava1a48f0c2025-02-19 19:02:03 +0530409 try
410 {
411 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
412 if (l_imValue.empty())
413 {
414 throw DbusException("Invalid IM value read from Dbus");
415 }
Sunny Srivastavac6ef42d2025-02-19 19:17:10 +0530416
417 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
418 {
419 // TODO: Should booting be blocked in case of some
420 // misconfigurations?
421 return;
422 }
Sunny Srivastava022112b2025-02-19 19:53:29 +0530423
424 const nlohmann::json& l_powerVsJsonObj =
425 jsonUtility::getPowerVsJson(l_imValue);
426
427 if (l_powerVsJsonObj.empty())
428 {
429 throw std::runtime_error("PowerVS Json not found");
430 }
431
432 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
433
434 if (!l_failedPathList.empty())
435 {
436 throw std::runtime_error(
437 "Part number update failed for following paths: ");
438 }
Sunny Srivastava1a48f0c2025-02-19 19:02:03 +0530439 }
440 catch (const std::exception& l_ex)
441 {
442 // TODO log appropriate PEL
443 }
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530444}
Sunny Srivastava46f29812025-02-25 11:36:17 +0530445
446void Manager::processFailedEeproms()
447{
448 if (m_worker.get() != nullptr)
449 {
450 // TODO:
451 // - iterate through list of EEPROMs for which thread creation has
452 // failed
453 // - For each failed EEPROM, trigger VPD collection
454 m_worker->getFailedEepromPaths().clear();
455 }
456}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500457#endif
458
459int Manager::updateKeyword(const types::Path i_vpdPath,
460 const types::WriteVpdParams i_paramsToWriteData)
461{
462 if (i_vpdPath.empty())
463 {
464 logging::logMessage("Given VPD path is empty.");
465 return -1;
466 }
467
468 types::Path l_fruPath;
469 nlohmann::json l_sysCfgJsonObj{};
470
471 if (m_worker.get() != nullptr)
472 {
473 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
474
475 // Get the EEPROM path
476 if (!l_sysCfgJsonObj.empty())
477 {
478 try
479 {
480 l_fruPath =
481 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
482 }
483 catch (const std::exception& l_exception)
484 {
485 logging::logMessage(
486 "Error while getting FRU path, Path: " + i_vpdPath +
487 ", error: " + std::string(l_exception.what()));
488 return -1;
489 }
490 }
491 }
492
493 if (l_fruPath.empty())
494 {
495 l_fruPath = i_vpdPath;
496 }
497
498 try
499 {
500 std::shared_ptr<Parser> l_parserObj =
501 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
502 return l_parserObj->updateVpdKeyword(i_paramsToWriteData);
503 }
504 catch (const std::exception& l_exception)
505 {
506 // TODO:: error log needed
507 logging::logMessage("Update keyword failed for file[" + i_vpdPath +
508 "], reason: " + std::string(l_exception.what()));
509 return -1;
510 }
511}
512
513int Manager::updateKeywordOnHardware(
514 const types::Path i_fruPath,
515 const types::WriteVpdParams i_paramsToWriteData) noexcept
516{
517 try
518 {
519 if (i_fruPath.empty())
520 {
521 throw std::runtime_error("Given FRU path is empty");
522 }
523
524 nlohmann::json l_sysCfgJsonObj{};
525
526 if (m_worker.get() != nullptr)
527 {
528 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
529 }
530
531 std::shared_ptr<Parser> l_parserObj =
532 std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
533 return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
534 }
535 catch (const std::exception& l_exception)
536 {
537 EventLogger::createAsyncPel(
538 types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
539 __FILE__, __FUNCTION__, 0,
540 "Update keyword on hardware failed for file[" + i_fruPath +
541 "], reason: " + std::string(l_exception.what()),
542 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
543
544 return constants::FAILURE;
545 }
546}
547
548types::DbusVariantType Manager::readKeyword(
549 const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
550{
551 try
552 {
553 nlohmann::json l_jsonObj{};
554
555 if (m_worker.get() != nullptr)
556 {
557 l_jsonObj = m_worker->getSysCfgJsonObj();
558 }
559
560 std::error_code ec;
561
562 // Check if given path is filesystem path
563 if (!std::filesystem::exists(i_fruPath, ec) && (ec))
564 {
565 throw std::runtime_error(
566 "Given file path " + i_fruPath + " not found.");
567 }
568
569 logging::logMessage("Performing VPD read on " + i_fruPath);
570
571 std::shared_ptr<vpd::Parser> l_parserObj =
572 std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
573
574 std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
575 l_parserObj->getVpdParserInstance();
576
577 return (
578 l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
579 }
580 catch (const std::exception& e)
581 {
582 logging::logMessage(
583 e.what() + std::string(". VPD manager read operation failed for ") +
584 i_fruPath);
585 throw types::DeviceError::ReadFailure();
586 }
587}
588
589void Manager::collectSingleFruVpd(
590 const sdbusplus::message::object_path& i_dbusObjPath)
591{
592 try
593 {
594 if (m_vpdCollectionStatus != "Completed")
595 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600596 logging::logMessage(
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500597 "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
598 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600599 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500600 }
601
602 // Get system config JSON object from worker class
603 nlohmann::json l_sysCfgJsonObj{};
604
605 if (m_worker.get() != nullptr)
606 {
607 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
608 }
609
610 // Check if system config JSON is present
611 if (l_sysCfgJsonObj.empty())
612 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600613 logging::logMessage(
614 "System config JSON object not present. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500615 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600616 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500617 }
618
619 // Get FRU path for the given D-bus object path from JSON
620 const std::string& l_fruPath =
621 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_dbusObjPath);
622
623 if (l_fruPath.empty())
624 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600625 logging::logMessage(
626 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500627 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600628 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500629 }
630
631 // Check if host is up and running
632 if (dbusUtility::isHostRunning())
633 {
634 if (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
635 l_fruPath))
636 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600637 logging::logMessage(
638 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500639 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600640 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500641 }
642 }
643 else if (dbusUtility::isBMCReady())
644 {
645 if (!jsonUtility::isFruReplaceableAtStandby(l_sysCfgJsonObj,
646 l_fruPath) &&
647 (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
648 l_fruPath)))
649 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600650 logging::logMessage(
651 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500652 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600653 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500654 }
655 }
656
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600657 // Set CollectionStatus as InProgress. Since it's an intermediate state
658 // D-bus set-property call is good enough to update the status.
RekhaAparna01c532b182025-02-19 19:56:49 -0600659 const std::string& l_collStatusProp = "CollectionStatus";
660
661 if (!dbusUtility::writeDbusProperty(
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600662 jsonUtility::getServiceName(l_sysCfgJsonObj,
663 std::string(i_dbusObjPath)),
664 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
665 l_collStatusProp,
RekhaAparna01c532b182025-02-19 19:56:49 -0600666 types::DbusVariantType{constants::vpdCollectionInProgress}))
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600667 {
668 logging::logMessage(
669 "Unable to set CollectionStatus as InProgress for " +
670 std::string(i_dbusObjPath) +
671 ". Continue single FRU VPD collection.");
672 }
673
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500674 // Parse VPD
675 types::VPDMapVariant l_parsedVpd = m_worker->parseVpdFile(l_fruPath);
676
677 // If l_parsedVpd is pointing to std::monostate
678 if (l_parsedVpd.index() == 0)
679 {
680 throw std::runtime_error(
681 "VPD parsing failed for " + std::string(i_dbusObjPath));
682 }
683
684 // Get D-bus object map from worker class
685 types::ObjectMap l_dbusObjectMap;
686 m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
687
688 if (l_dbusObjectMap.empty())
689 {
690 throw std::runtime_error(
691 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
692 std::string(i_dbusObjPath));
693 }
694
695 // Call PIM's Notify method
696 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
697 {
698 throw std::runtime_error(
699 "Notify PIM failed. Single FRU VPD collection failed for " +
700 std::string(i_dbusObjPath));
701 }
702 }
703 catch (const std::exception& l_error)
704 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600705 // Notify FRU's VPD CollectionStatus as Failure
706 if (!dbusUtility::notifyFRUCollectionStatus(
707 std::string(i_dbusObjPath), constants::vpdCollectionFailure))
708 {
709 logging::logMessage(
710 "Call to PIM Notify method failed to update Collection status as Failure for " +
711 std::string(i_dbusObjPath));
712 }
713
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500714 // TODO: Log PEL
715 logging::logMessage(std::string(l_error.what()));
716 }
717}
718
719void Manager::deleteSingleFruVpd(
720 const sdbusplus::message::object_path& i_dbusObjPath)
721{
722 try
723 {
724 if (std::string(i_dbusObjPath).empty())
725 {
726 throw std::runtime_error(
727 "Given DBus object path is empty. Aborting FRU VPD deletion.");
728 }
729
730 if (m_worker.get() == nullptr)
731 {
732 throw std::runtime_error(
733 "Worker object not found, can't perform FRU VPD deletion for: " +
734 std::string(i_dbusObjPath));
735 }
736
737 m_worker->deleteFruVpd(std::string(i_dbusObjPath));
738 }
739 catch (const std::exception& l_ex)
740 {
741 // TODO: Log PEL
742 logging::logMessage(l_ex.what());
743 }
744}
745
746bool Manager::isValidUnexpandedLocationCode(
747 const std::string& i_unexpandedLocationCode)
748{
749 if ((i_unexpandedLocationCode.length() <
750 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
751 ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
752 constants::STR_CMP_SUCCESS) &&
753 (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
754 constants::STR_CMP_SUCCESS)) ||
755 ((i_unexpandedLocationCode.length() >
756 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
757 (i_unexpandedLocationCode.find("-") != 4)))
758 {
759 return false;
760 }
761
762 return true;
763}
764
765std::string Manager::getExpandedLocationCode(
766 const std::string& i_unexpandedLocationCode,
767 [[maybe_unused]] const uint16_t i_nodeNumber)
768{
769 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
770 {
771 phosphor::logging::elog<types::DbusInvalidArgument>(
772 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
773 types::InvalidArgument::ARGUMENT_VALUE(
774 i_unexpandedLocationCode.c_str()));
775 }
776
777 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
778 if (!l_sysCfgJsonObj.contains("frus"))
779 {
780 logging::logMessage("Missing frus tag in system config JSON");
781 }
782
783 const nlohmann::json& l_listOfFrus =
784 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
785
786 for (const auto& l_frus : l_listOfFrus.items())
787 {
788 for (const auto& l_aFru : l_frus.value())
789 {
790 if (l_aFru["extraInterfaces"].contains(
791 constants::locationCodeInf) &&
792 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
793 "LocationCode", "") == i_unexpandedLocationCode)
794 {
795 return std::get<std::string>(dbusUtility::readDbusProperty(
796 l_aFru["serviceName"], l_aFru["inventoryPath"],
797 constants::locationCodeInf, "LocationCode"));
798 }
799 }
800 }
801 phosphor::logging::elog<types::DbusInvalidArgument>(
802 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
803 types::InvalidArgument::ARGUMENT_VALUE(
804 i_unexpandedLocationCode.c_str()));
805}
806
807types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
808 const std::string& i_unexpandedLocationCode,
809 [[maybe_unused]] const uint16_t i_nodeNumber)
810{
811 types::ListOfPaths l_inventoryPaths;
812
813 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
814 {
815 phosphor::logging::elog<types::DbusInvalidArgument>(
816 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
817 types::InvalidArgument::ARGUMENT_VALUE(
818 i_unexpandedLocationCode.c_str()));
819 }
820
821 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
822 if (!l_sysCfgJsonObj.contains("frus"))
823 {
824 logging::logMessage("Missing frus tag in system config JSON");
825 }
826
827 const nlohmann::json& l_listOfFrus =
828 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
829
830 for (const auto& l_frus : l_listOfFrus.items())
831 {
832 for (const auto& l_aFru : l_frus.value())
833 {
834 if (l_aFru["extraInterfaces"].contains(
835 constants::locationCodeInf) &&
836 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
837 "LocationCode", "") == i_unexpandedLocationCode)
838 {
839 l_inventoryPaths.push_back(
840 l_aFru.at("inventoryPath")
841 .get_ref<const nlohmann::json::string_t&>());
842 }
843 }
844 }
845
846 if (l_inventoryPaths.empty())
847 {
848 phosphor::logging::elog<types::DbusInvalidArgument>(
849 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
850 types::InvalidArgument::ARGUMENT_VALUE(
851 i_unexpandedLocationCode.c_str()));
852 }
853
854 return l_inventoryPaths;
855}
856
Patrick Williams43fedab2025-02-03 14:28:05 -0500857std::string Manager::getHwPath(
858 const sdbusplus::message::object_path& i_dbusObjPath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500859{
860 // Dummy code to supress unused variable warning. To be removed.
861 logging::logMessage(std::string(i_dbusObjPath));
862
863 return std::string{};
864}
865
866std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
867 const std::string& i_expandedLocationCode)
868{
869 /**
870 * Location code should always start with U and fulfil minimum length
871 * criteria.
872 */
873 if (i_expandedLocationCode[0] != 'U' ||
874 i_expandedLocationCode.length() <
875 constants::EXP_LOCATION_CODE_MIN_LENGTH)
876 {
877 phosphor::logging::elog<types::DbusInvalidArgument>(
878 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
879 types::InvalidArgument::ARGUMENT_VALUE(
880 i_expandedLocationCode.c_str()));
881 }
882
883 std::string l_fcKwd;
884
885 auto l_fcKwdValue = dbusUtility::readDbusProperty(
886 "xyz.openbmc_project.Inventory.Manager",
887 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
888 "com.ibm.ipzvpd.VCEN", "FC");
889
890 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
891 {
892 l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
893 }
894
895 // Get the first part of expanded location code to check for FC or TM.
896 std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
897
898 std::string l_unexpandedLocationCode{};
899 uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
900
901 // Check if this value matches the value of FC keyword.
902 if (l_fcKwd.substr(0, 4) == l_firstKwd)
903 {
904 /**
905 * Period(.) should be there in expanded location code to seggregate
906 * FC, node number and SE values.
907 */
908 size_t l_nodeStartPos = i_expandedLocationCode.find('.');
909 if (l_nodeStartPos == std::string::npos)
910 {
911 phosphor::logging::elog<types::DbusInvalidArgument>(
912 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
913 types::InvalidArgument::ARGUMENT_VALUE(
914 i_expandedLocationCode.c_str()));
915 }
916
917 size_t l_nodeEndPos =
918 i_expandedLocationCode.find('.', l_nodeStartPos + 1);
919 if (l_nodeEndPos == std::string::npos)
920 {
921 phosphor::logging::elog<types::DbusInvalidArgument>(
922 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
923 types::InvalidArgument::ARGUMENT_VALUE(
924 i_expandedLocationCode.c_str()));
925 }
926
927 // Skip 3 bytes for '.ND'
928 l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
929 l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
930
931 /**
932 * Confirm if there are other details apart FC, node number and SE
933 * in location code
934 */
935 if (i_expandedLocationCode.length() >
936 constants::EXP_LOCATION_CODE_MIN_LENGTH)
937 {
938 l_unexpandedLocationCode =
939 i_expandedLocationCode[0] + std::string("fcs") +
940 i_expandedLocationCode.substr(
941 l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
942 std::string::npos);
943 }
944 else
945 {
946 l_unexpandedLocationCode = "Ufcs";
947 }
948 }
949 else
950 {
951 std::string l_tmKwd;
952 // Read TM keyword value.
953 auto l_tmKwdValue = dbusUtility::readDbusProperty(
954 "xyz.openbmc_project.Inventory.Manager",
955 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
956 "com.ibm.ipzvpd.VSYS", "TM");
957
958 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
959 {
960 l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
961 }
962
963 // Check if the substr matches to TM keyword value.
964 if (l_tmKwd.substr(0, 4) == l_firstKwd)
965 {
966 /**
967 * System location code will not have node number and any other
968 * details.
969 */
970 l_unexpandedLocationCode = "Umts";
971 }
972 // The given location code is neither "fcs" or "mts".
973 else
974 {
975 phosphor::logging::elog<types::DbusInvalidArgument>(
976 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
977 types::InvalidArgument::ARGUMENT_VALUE(
978 i_expandedLocationCode.c_str()));
979 }
980 }
981
982 return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
983}
984
985types::ListOfPaths Manager::getFrusByExpandedLocationCode(
986 const std::string& i_expandedLocationCode)
987{
988 std::tuple<std::string, uint16_t> l_locationAndNodePair =
989 getUnexpandedLocationCode(i_expandedLocationCode);
990
991 return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
992 std::get<1>(l_locationAndNodePair));
993}
994
995void Manager::registerHostStateChangeCallback()
996{
997 static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
998 std::make_shared<sdbusplus::bus::match_t>(
999 *m_asioConnection,
1000 sdbusplus::bus::match::rules::propertiesChanged(
1001 constants::hostObjectPath, constants::hostInterface),
1002 [this](sdbusplus::message_t& i_msg) {
1003 hostStateChangeCallBack(i_msg);
1004 });
1005}
1006
1007void Manager::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
1008{
1009 try
1010 {
1011 if (i_msg.is_method_error())
1012 {
1013 throw std::runtime_error(
1014 "Error reading callback message for host state");
1015 }
1016
1017 std::string l_objectPath;
1018 types::PropertyMap l_propMap;
1019 i_msg.read(l_objectPath, l_propMap);
1020
1021 const auto l_itr = l_propMap.find("CurrentHostState");
1022
1023 if (l_itr == l_propMap.end())
1024 {
1025 throw std::runtime_error(
1026 "CurrentHostState field is missing in callback message");
1027 }
1028
1029 if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
1030 {
1031 // implies system is moving from standby to power on state
1032 if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
1033 "TransitioningToRunning")
1034 {
1035 // TODO: check for all the essential FRUs in the system.
1036
1037 // Perform recollection.
1038 performVpdRecollection();
1039 return;
1040 }
1041 }
1042 else
1043 {
1044 throw std::runtime_error(
1045 "Invalid type recieved in variant for host state.");
1046 }
1047 }
1048 catch (const std::exception& l_ex)
1049 {
1050 // TODO: Log PEL.
1051 logging::logMessage(l_ex.what());
1052 }
1053}
1054
1055void Manager::performVpdRecollection()
1056{
1057 try
1058 {
1059 if (m_worker.get() != nullptr)
1060 {
1061 nlohmann::json l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
1062
1063 // Check if system config JSON is present
1064 if (l_sysCfgJsonObj.empty())
1065 {
1066 throw std::runtime_error(
1067 "System config json object is empty, can't process recollection.");
1068 }
1069
1070 const auto& l_frusReplaceableAtStandby =
1071 jsonUtility::getListOfFrusReplaceableAtStandby(l_sysCfgJsonObj);
1072
1073 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1074 {
1075 // ToDo: Add some logic/trace to know the flow to
1076 // collectSingleFruVpd has been directed via
1077 // performVpdRecollection.
1078 collectSingleFruVpd(l_fruInventoryPath);
1079 }
1080 return;
1081 }
1082
1083 throw std::runtime_error(
1084 "Worker object not found can't process recollection");
1085 }
1086 catch (const std::exception& l_ex)
1087 {
1088 // TODO Log PEL
1089 logging::logMessage(
1090 "VPD recollection failed with error: " + std::string(l_ex.what()));
1091 }
1092}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001093} // namespace vpd