blob: be62094a9fdcca417fab87a4000019dfafa962a3 [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"
Rekha Aparnaffdff312025-03-25 01:10:56 -050012#include "single_fab.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050013#include "types.hpp"
14#include "utility/dbus_utility.hpp"
15#include "utility/json_utility.hpp"
16#include "utility/vpd_specific_utility.hpp"
17
18#include <boost/asio/steady_timer.hpp>
19#include <sdbusplus/bus/match.hpp>
20#include <sdbusplus/message.hpp>
21
22namespace vpd
23{
24Manager::Manager(
25 const std::shared_ptr<boost::asio::io_context>& ioCon,
26 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
27 const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
28 m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection)
29{
Rekha Aparnaffdff312025-03-25 01:10:56 -050030#ifdef IBM_SYSTEM
31 if (!dbusUtility::isChassisPowerOn())
32 {
33 SingleFab l_singleFab;
34 const int& l_rc = l_singleFab.singleFabImOverride();
35
36 if (l_rc == constants::FAILURE)
37 {
38 throw std::runtime_error(
39 std::string(__FUNCTION__) +
40 " : Found an invalid system configuration. Needs manual intervention. BMC is being quiesced.");
41 }
42 }
43#endif
44
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050045 try
46 {
47#ifdef IBM_SYSTEM
Sunny Srivastava765cf7b2025-02-04 05:24:11 -060048 if (dbusUtility::isChassisPowerOn())
49 {
50 // At power on, less number of FRU(s) needs collection. we can scale
51 // down the threads to reduce CPU utilization.
52 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT,
53 constants::VALUE_1);
54 }
55 else
56 {
57 // Initialize with default configuration
58 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
59 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050060
61 // Set up minimal things that is needed before bus name is claimed.
62 m_worker->performInitialSetup();
63
64 // set callback to detect any asset tag change
65 registerAssetTagChangeCallback();
66
67 // 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 = std::make_shared<GpioMonitor>(
75 m_worker->getSysCfgJsonObj(), m_worker, m_ioContext);
76
77#endif
78 // set callback to detect host state change.
79 registerHostStateChangeCallback();
80
81 // For backward compatibility. Should be depricated.
82 iFace->register_method(
83 "WriteKeyword",
84 [this](const sdbusplus::message::object_path i_path,
85 const std::string i_recordName, const std::string i_keyword,
86 const types::BinaryVector i_value) -> int {
87 return this->updateKeyword(
88 i_path, std::make_tuple(i_recordName, i_keyword, i_value));
89 });
90
91 // Register methods under com.ibm.VPD.Manager interface
92 iFace->register_method(
93 "UpdateKeyword",
94 [this](const types::Path i_vpdPath,
95 const types::WriteVpdParams i_paramsToWriteData) -> int {
96 return this->updateKeyword(i_vpdPath, i_paramsToWriteData);
97 });
98
99 iFace->register_method(
100 "WriteKeywordOnHardware",
101 [this](const types::Path i_fruPath,
102 const types::WriteVpdParams i_paramsToWriteData) -> int {
103 return this->updateKeywordOnHardware(i_fruPath,
104 i_paramsToWriteData);
105 });
106
107 iFace->register_method(
108 "ReadKeyword",
109 [this](const types::Path i_fruPath,
110 const types::ReadVpdParams i_paramsToReadData)
111 -> types::DbusVariantType {
112 return this->readKeyword(i_fruPath, i_paramsToReadData);
113 });
114
115 iFace->register_method(
116 "CollectFRUVPD",
117 [this](const sdbusplus::message::object_path& i_dbusObjPath) {
118 this->collectSingleFruVpd(i_dbusObjPath);
119 });
120
121 iFace->register_method(
122 "deleteFRUVPD",
123 [this](const sdbusplus::message::object_path& i_dbusObjPath) {
124 this->deleteSingleFruVpd(i_dbusObjPath);
125 });
126
127 iFace->register_method(
128 "GetExpandedLocationCode",
129 [this](const std::string& i_unexpandedLocationCode,
130 uint16_t& i_nodeNumber) -> std::string {
131 return this->getExpandedLocationCode(i_unexpandedLocationCode,
132 i_nodeNumber);
133 });
134
135 iFace->register_method("GetFRUsByExpandedLocationCode",
136 [this](const std::string& i_expandedLocationCode)
137 -> types::ListOfPaths {
138 return this->getFrusByExpandedLocationCode(
139 i_expandedLocationCode);
140 });
141
142 iFace->register_method(
143 "GetFRUsByUnexpandedLocationCode",
144 [this](const std::string& i_unexpandedLocationCode,
145 uint16_t& i_nodeNumber) -> types::ListOfPaths {
146 return this->getFrusByUnexpandedLocationCode(
147 i_unexpandedLocationCode, i_nodeNumber);
148 });
149
150 iFace->register_method(
151 "GetHardwarePath",
152 [this](const sdbusplus::message::object_path& i_dbusObjPath)
153 -> std::string { return this->getHwPath(i_dbusObjPath); });
154
155 iFace->register_method("PerformVPDRecollection", [this]() {
156 this->performVpdRecollection();
157 });
158
159 // Indicates FRU VPD collection for the system has not started.
160 iFace->register_property_rw<std::string>(
161 "CollectionStatus", sdbusplus::vtable::property_::emits_change,
162 [this](const std::string l_currStatus, const auto&) {
163 m_vpdCollectionStatus = l_currStatus;
164 return 0;
165 },
166 [this](const auto&) { return m_vpdCollectionStatus; });
167 }
168 catch (const std::exception& e)
169 {
170 logging::logMessage(
171 "VPD-Manager service failed. " + std::string(e.what()));
172 throw;
173 }
174}
175
176#ifdef IBM_SYSTEM
177void Manager::registerAssetTagChangeCallback()
178{
179 static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
180 std::make_shared<sdbusplus::bus::match_t>(
181 *m_asioConnection,
182 sdbusplus::bus::match::rules::propertiesChanged(
183 constants::systemInvPath, constants::assetTagInf),
184 [this](sdbusplus::message_t& l_msg) {
185 processAssetTagChangeCallback(l_msg);
186 });
187}
188
189void Manager::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
190{
191 try
192 {
193 if (i_msg.is_method_error())
194 {
195 throw std::runtime_error(
196 "Error reading callback msg for asset tag.");
197 }
198
199 std::string l_objectPath;
200 types::PropertyMap l_propMap;
201 i_msg.read(l_objectPath, l_propMap);
202
203 const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
204 if (l_itrToAssetTag != l_propMap.end())
205 {
206 if (auto l_assetTag =
207 std::get_if<std::string>(&(l_itrToAssetTag->second)))
208 {
209 // Call Notify to persist the AssetTag
210 types::ObjectMap l_objectMap = {
211 {sdbusplus::message::object_path(constants::systemInvPath),
212 {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
213
214 // Notify PIM
215 if (!dbusUtility::callPIM(move(l_objectMap)))
216 {
217 throw std::runtime_error(
218 "Call to PIM failed for asset tag update.");
219 }
220 }
221 }
222 else
223 {
224 throw std::runtime_error(
225 "Could not find asset tag in callback message.");
226 }
227 }
228 catch (const std::exception& l_ex)
229 {
230 // TODO: Log PEL with below description.
231 logging::logMessage("Asset tag callback update failed with error: " +
232 std::string(l_ex.what()));
233 }
234}
235
236void Manager::SetTimerToDetectSVPDOnDbus()
237{
Sunny Srivastava58057c12025-03-19 10:41:34 +0530238 try
239 {
240 static boost::asio::steady_timer timer(*m_ioContext);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500241
Sunny Srivastava58057c12025-03-19 10:41:34 +0530242 // timer for 2 seconds
243 auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500244
Sunny Srivastava58057c12025-03-19 10:41:34 +0530245 (asyncCancelled == 0) ? logging::logMessage("Timer started")
246 : logging::logMessage("Timer re-started");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500247
Sunny Srivastava58057c12025-03-19 10:41:34 +0530248 timer.async_wait([this](const boost::system::error_code& ec) {
249 if (ec == boost::asio::error::operation_aborted)
250 {
251 throw std::runtime_error(
252 std::string(__FUNCTION__) +
253 ": Timer to detect system VPD collection status was aborted.");
254 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500255
Sunny Srivastava58057c12025-03-19 10:41:34 +0530256 if (ec)
257 {
258 throw std::runtime_error(
259 std::string(__FUNCTION__) +
260 ": Timer to detect System VPD collection failed");
261 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500262
Sunny Srivastava58057c12025-03-19 10:41:34 +0530263 if (m_worker->isSystemVPDOnDBus())
264 {
265 // cancel the timer
266 timer.cancel();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500267
Sunny Srivastava58057c12025-03-19 10:41:34 +0530268 // Triggering FRU VPD collection. Setting status to "In
269 // Progress".
270 m_interface->set_property("CollectionStatus",
271 std::string("InProgress"));
272 m_worker->collectFrusFromJson();
273 }
274 });
275 }
276 catch (const std::exception& l_ex)
277 {
278 EventLogger::createAsyncPel(
279 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
280 __FILE__, __FUNCTION__, 0,
281 std::string("Collection for FRUs failed with reason:") +
282 EventLogger::getErrorMsg(l_ex),
283 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
284 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500285}
286
287void Manager::SetTimerToDetectVpdCollectionStatus()
288{
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600289 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500290 // system type.
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600291 static constexpr auto MAX_RETRY = 12;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500292
293 static boost::asio::steady_timer l_timer(*m_ioContext);
294 static uint8_t l_timerRetry = 0;
295
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600296 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500297
298 (l_asyncCancelled == 0)
299 ? logging::logMessage("Collection Timer started")
300 : logging::logMessage("Collection Timer re-started");
301
302 l_timer.async_wait([this](const boost::system::error_code& ec) {
303 if (ec == boost::asio::error::operation_aborted)
304 {
305 throw std::runtime_error(
306 "Timer to detect thread collection status was aborted");
307 }
308
309 if (ec)
310 {
311 throw std::runtime_error(
312 "Timer to detect thread collection failed");
313 }
314
315 if (m_worker->isAllFruCollectionDone())
316 {
317 // cancel the timer
318 l_timer.cancel();
Souvik Roy1f4c8f82025-01-23 00:37:43 -0600319 processFailedEeproms();
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530320
321 // update VPD for powerVS system.
322 ConfigurePowerVsSystem();
323
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500324 m_interface->set_property("CollectionStatus",
325 std::string("Completed"));
326
327 const nlohmann::json& l_sysCfgJsonObj =
328 m_worker->getSysCfgJsonObj();
329 if (jsonUtility::isBackupAndRestoreRequired(l_sysCfgJsonObj))
330 {
331 BackupAndRestore l_backupAndRestoreObj(l_sysCfgJsonObj);
332 l_backupAndRestoreObj.backupAndRestore();
333 }
334 }
335 else
336 {
337 auto l_threadCount = m_worker->getActiveThreadCount();
338 if (l_timerRetry == MAX_RETRY)
339 {
340 l_timer.cancel();
341 logging::logMessage("Taking too long. Active thread = " +
342 std::to_string(l_threadCount));
343 }
344 else
345 {
346 l_timerRetry++;
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600347 logging::logMessage("Collection is in progress for [" +
348 std::to_string(l_threadCount) + "] FRUs.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500349
350 SetTimerToDetectVpdCollectionStatus();
351 }
352 }
353 });
354}
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530355
Sunny Srivastava022112b2025-02-19 19:53:29 +0530356void Manager::checkAndUpdatePowerVsVpd(
357 const nlohmann::json& i_powerVsJsonObj,
358 std::vector<std::string>& o_failedPathList)
359{
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530360 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
361 {
Sunny Srivastavaf1dda762025-02-19 23:46:19 +0530362 nlohmann::json l_sysCfgJsonObj{};
363 if (m_worker.get() != nullptr)
364 {
365 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
366 }
367
368 // The utility method will handle emty JSON case. No explicit
369 // handling required here.
370 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
371 l_sysCfgJsonObj, l_fruPath);
372
373 // Mark it as failed if inventory path not found in JSON.
374 if (l_inventoryPath.empty())
375 {
376 o_failedPathList.push_back(l_fruPath);
377 continue;
378 }
379
380 // check if the FRU is present
381 if (!dbusUtility::isInventoryPresent(l_inventoryPath))
382 {
383 logging::logMessage(
384 "Inventory not present, skip updating part number. Path: " +
385 l_inventoryPath);
386 continue;
387 }
388
389 // check if the FRU needs CCIN check before updating PN.
390 if (l_recJson.contains("CCIN"))
391 {
392 const auto& l_ccinFromDbus =
393 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath);
394
395 // Not an ideal situation as CCIN can't be empty.
396 if (l_ccinFromDbus.empty())
397 {
398 o_failedPathList.push_back(l_fruPath);
399 continue;
400 }
401
402 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
403
404 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
405 l_ccinFromDbus) == l_ccinListFromJson.end())
406 {
407 // Don't update PN in this case.
408 continue;
409 }
410 }
411
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530412 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
413 {
Sunny Srivastavaf1dda762025-02-19 23:46:19 +0530414 // Record name can't be CCIN, skip processing as it is there for PN
415 // update based on CCIN check.
416 if (l_recordName == constants::kwdCCIN)
417 {
418 continue;
419 }
420
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530421 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
422 {
423 // Is value of type array.
424 if (!l_kwdValue.is_array())
425 {
426 o_failedPathList.push_back(l_fruPath);
427 continue;
428 }
429
Sunny Srivastava22793832025-03-20 12:09:09 +0530430 // Get current FRU Part number.
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530431 auto l_retVal = dbusUtility::readDbusProperty(
432 constants::pimServiceName, l_inventoryPath,
Sunny Srivastava22793832025-03-20 12:09:09 +0530433 constants::viniInf, constants::kwdFN);
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530434
Sunny Srivastava22793832025-03-20 12:09:09 +0530435 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530436
Sunny Srivastava22793832025-03-20 12:09:09 +0530437 if (!l_ptrToFn)
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530438 {
439 o_failedPathList.push_back(l_fruPath);
440 continue;
441 }
442
443 types::BinaryVector l_binaryKwdValue =
444 l_kwdValue.get<types::BinaryVector>();
Sunny Srivastava22793832025-03-20 12:09:09 +0530445 if (l_binaryKwdValue == (*l_ptrToFn))
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530446 {
447 continue;
448 }
449
450 // Update part number only if required.
451 if (updateKeyword(
452 l_fruPath,
453 std::make_tuple(l_recordName, l_kwdName, l_kwdValue)) ==
454 constants::FAILURE)
455 {
456 o_failedPathList.push_back(l_fruPath);
457 continue;
458 }
459
Sunny Srivastava22793832025-03-20 12:09:09 +0530460 // update the Asset interface Spare part number explicitly.
Sunny Srivastavab83cb352025-03-06 14:41:27 +0530461 if (!dbusUtility::callPIM(types::ObjectMap{
462 {l_inventoryPath,
Sunny Srivastava22793832025-03-20 12:09:09 +0530463 {{constants::assetInf,
464 {{"SparePartNumber",
Sunny Srivastavab83cb352025-03-06 14:41:27 +0530465 std::string(l_binaryKwdValue.begin(),
466 l_binaryKwdValue.end())}}}}}}))
467 {
468 logging::logMessage(
Sunny Srivastava22793832025-03-20 12:09:09 +0530469 "Updating Spare Part Number under Asset interface failed for path [" +
Sunny Srivastavab83cb352025-03-06 14:41:27 +0530470 l_inventoryPath + "]");
471 }
472
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530473 // Just needed for logging.
Sunny Srivastava22793832025-03-20 12:09:09 +0530474 std::string l_initialPartNum((*l_ptrToFn).begin(),
475 (*l_ptrToFn).end());
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530476 std::string l_finalPartNum(l_binaryKwdValue.begin(),
477 l_binaryKwdValue.end());
478 logging::logMessage(
Sunny Srivastava22793832025-03-20 12:09:09 +0530479 "FRU Part number updated for path [" + l_inventoryPath +
480 "]" + "From [" + l_initialPartNum + "]" + " to [" +
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530481 l_finalPartNum + "]");
482 }
483 }
484 }
Sunny Srivastava022112b2025-02-19 19:53:29 +0530485}
486
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530487void Manager::ConfigurePowerVsSystem()
488{
Sunny Srivastava022112b2025-02-19 19:53:29 +0530489 std::vector<std::string> l_failedPathList;
Sunny Srivastava1a48f0c2025-02-19 19:02:03 +0530490 try
491 {
492 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
493 if (l_imValue.empty())
494 {
495 throw DbusException("Invalid IM value read from Dbus");
496 }
Sunny Srivastavac6ef42d2025-02-19 19:17:10 +0530497
498 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
499 {
500 // TODO: Should booting be blocked in case of some
501 // misconfigurations?
502 return;
503 }
Sunny Srivastava022112b2025-02-19 19:53:29 +0530504
505 const nlohmann::json& l_powerVsJsonObj =
506 jsonUtility::getPowerVsJson(l_imValue);
507
508 if (l_powerVsJsonObj.empty())
509 {
510 throw std::runtime_error("PowerVS Json not found");
511 }
512
513 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
514
515 if (!l_failedPathList.empty())
516 {
517 throw std::runtime_error(
518 "Part number update failed for following paths: ");
519 }
Sunny Srivastava1a48f0c2025-02-19 19:02:03 +0530520 }
521 catch (const std::exception& l_ex)
522 {
523 // TODO log appropriate PEL
524 }
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530525}
Sunny Srivastava46f29812025-02-25 11:36:17 +0530526
527void Manager::processFailedEeproms()
528{
529 if (m_worker.get() != nullptr)
530 {
531 // TODO:
532 // - iterate through list of EEPROMs for which thread creation has
533 // failed
534 // - For each failed EEPROM, trigger VPD collection
535 m_worker->getFailedEepromPaths().clear();
536 }
537}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500538#endif
539
540int Manager::updateKeyword(const types::Path i_vpdPath,
541 const types::WriteVpdParams i_paramsToWriteData)
542{
543 if (i_vpdPath.empty())
544 {
545 logging::logMessage("Given VPD path is empty.");
546 return -1;
547 }
548
549 types::Path l_fruPath;
550 nlohmann::json l_sysCfgJsonObj{};
551
552 if (m_worker.get() != nullptr)
553 {
554 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
555
556 // Get the EEPROM path
557 if (!l_sysCfgJsonObj.empty())
558 {
RekhaAparna017fea9f52025-02-17 04:14:02 -0600559 l_fruPath =
560 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500561 }
562 }
563
564 if (l_fruPath.empty())
565 {
566 l_fruPath = i_vpdPath;
567 }
568
569 try
570 {
571 std::shared_ptr<Parser> l_parserObj =
572 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
573 return l_parserObj->updateVpdKeyword(i_paramsToWriteData);
574 }
575 catch (const std::exception& l_exception)
576 {
577 // TODO:: error log needed
578 logging::logMessage("Update keyword failed for file[" + i_vpdPath +
579 "], reason: " + std::string(l_exception.what()));
580 return -1;
581 }
582}
583
584int Manager::updateKeywordOnHardware(
585 const types::Path i_fruPath,
586 const types::WriteVpdParams i_paramsToWriteData) noexcept
587{
588 try
589 {
590 if (i_fruPath.empty())
591 {
592 throw std::runtime_error("Given FRU path is empty");
593 }
594
595 nlohmann::json l_sysCfgJsonObj{};
596
597 if (m_worker.get() != nullptr)
598 {
599 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
600 }
601
602 std::shared_ptr<Parser> l_parserObj =
603 std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
604 return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
605 }
606 catch (const std::exception& l_exception)
607 {
608 EventLogger::createAsyncPel(
609 types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
610 __FILE__, __FUNCTION__, 0,
611 "Update keyword on hardware failed for file[" + i_fruPath +
612 "], reason: " + std::string(l_exception.what()),
613 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
614
615 return constants::FAILURE;
616 }
617}
618
619types::DbusVariantType Manager::readKeyword(
620 const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
621{
622 try
623 {
624 nlohmann::json l_jsonObj{};
625
626 if (m_worker.get() != nullptr)
627 {
628 l_jsonObj = m_worker->getSysCfgJsonObj();
629 }
630
631 std::error_code ec;
632
633 // Check if given path is filesystem path
634 if (!std::filesystem::exists(i_fruPath, ec) && (ec))
635 {
636 throw std::runtime_error(
637 "Given file path " + i_fruPath + " not found.");
638 }
639
640 logging::logMessage("Performing VPD read on " + i_fruPath);
641
642 std::shared_ptr<vpd::Parser> l_parserObj =
643 std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
644
645 std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
646 l_parserObj->getVpdParserInstance();
647
648 return (
649 l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
650 }
651 catch (const std::exception& e)
652 {
653 logging::logMessage(
654 e.what() + std::string(". VPD manager read operation failed for ") +
655 i_fruPath);
656 throw types::DeviceError::ReadFailure();
657 }
658}
659
660void Manager::collectSingleFruVpd(
661 const sdbusplus::message::object_path& i_dbusObjPath)
662{
663 try
664 {
665 if (m_vpdCollectionStatus != "Completed")
666 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600667 logging::logMessage(
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500668 "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
669 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600670 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500671 }
672
673 // Get system config JSON object from worker class
674 nlohmann::json l_sysCfgJsonObj{};
675
676 if (m_worker.get() != nullptr)
677 {
678 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
679 }
680
681 // Check if system config JSON is present
682 if (l_sysCfgJsonObj.empty())
683 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600684 logging::logMessage(
685 "System config JSON object not present. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500686 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600687 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500688 }
689
690 // Get FRU path for the given D-bus object path from JSON
691 const std::string& l_fruPath =
692 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_dbusObjPath);
693
694 if (l_fruPath.empty())
695 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600696 logging::logMessage(
697 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500698 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600699 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500700 }
701
702 // Check if host is up and running
703 if (dbusUtility::isHostRunning())
704 {
705 if (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
706 l_fruPath))
707 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600708 logging::logMessage(
709 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500710 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600711 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500712 }
713 }
714 else if (dbusUtility::isBMCReady())
715 {
716 if (!jsonUtility::isFruReplaceableAtStandby(l_sysCfgJsonObj,
717 l_fruPath) &&
718 (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
719 l_fruPath)))
720 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600721 logging::logMessage(
722 "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 -0500723 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600724 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500725 }
726 }
727
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600728 // Set CollectionStatus as InProgress. Since it's an intermediate state
729 // D-bus set-property call is good enough to update the status.
RekhaAparna01c532b182025-02-19 19:56:49 -0600730 const std::string& l_collStatusProp = "CollectionStatus";
731
732 if (!dbusUtility::writeDbusProperty(
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600733 jsonUtility::getServiceName(l_sysCfgJsonObj,
734 std::string(i_dbusObjPath)),
735 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
736 l_collStatusProp,
RekhaAparna01c532b182025-02-19 19:56:49 -0600737 types::DbusVariantType{constants::vpdCollectionInProgress}))
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600738 {
739 logging::logMessage(
740 "Unable to set CollectionStatus as InProgress for " +
741 std::string(i_dbusObjPath) +
742 ". Continue single FRU VPD collection.");
743 }
744
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500745 // Parse VPD
746 types::VPDMapVariant l_parsedVpd = m_worker->parseVpdFile(l_fruPath);
747
748 // If l_parsedVpd is pointing to std::monostate
749 if (l_parsedVpd.index() == 0)
750 {
751 throw std::runtime_error(
752 "VPD parsing failed for " + std::string(i_dbusObjPath));
753 }
754
755 // Get D-bus object map from worker class
756 types::ObjectMap l_dbusObjectMap;
757 m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
758
759 if (l_dbusObjectMap.empty())
760 {
761 throw std::runtime_error(
762 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
763 std::string(i_dbusObjPath));
764 }
765
766 // Call PIM's Notify method
767 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
768 {
769 throw std::runtime_error(
770 "Notify PIM failed. Single FRU VPD collection failed for " +
771 std::string(i_dbusObjPath));
772 }
773 }
774 catch (const std::exception& l_error)
775 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600776 // Notify FRU's VPD CollectionStatus as Failure
777 if (!dbusUtility::notifyFRUCollectionStatus(
778 std::string(i_dbusObjPath), constants::vpdCollectionFailure))
779 {
780 logging::logMessage(
781 "Call to PIM Notify method failed to update Collection status as Failure for " +
782 std::string(i_dbusObjPath));
783 }
784
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500785 // TODO: Log PEL
786 logging::logMessage(std::string(l_error.what()));
787 }
788}
789
790void Manager::deleteSingleFruVpd(
791 const sdbusplus::message::object_path& i_dbusObjPath)
792{
793 try
794 {
795 if (std::string(i_dbusObjPath).empty())
796 {
797 throw std::runtime_error(
798 "Given DBus object path is empty. Aborting FRU VPD deletion.");
799 }
800
801 if (m_worker.get() == nullptr)
802 {
803 throw std::runtime_error(
804 "Worker object not found, can't perform FRU VPD deletion for: " +
805 std::string(i_dbusObjPath));
806 }
807
808 m_worker->deleteFruVpd(std::string(i_dbusObjPath));
809 }
810 catch (const std::exception& l_ex)
811 {
812 // TODO: Log PEL
813 logging::logMessage(l_ex.what());
814 }
815}
816
817bool Manager::isValidUnexpandedLocationCode(
818 const std::string& i_unexpandedLocationCode)
819{
820 if ((i_unexpandedLocationCode.length() <
821 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
822 ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
823 constants::STR_CMP_SUCCESS) &&
824 (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
825 constants::STR_CMP_SUCCESS)) ||
826 ((i_unexpandedLocationCode.length() >
827 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
828 (i_unexpandedLocationCode.find("-") != 4)))
829 {
830 return false;
831 }
832
833 return true;
834}
835
836std::string Manager::getExpandedLocationCode(
837 const std::string& i_unexpandedLocationCode,
838 [[maybe_unused]] const uint16_t i_nodeNumber)
839{
840 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
841 {
842 phosphor::logging::elog<types::DbusInvalidArgument>(
843 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
844 types::InvalidArgument::ARGUMENT_VALUE(
845 i_unexpandedLocationCode.c_str()));
846 }
847
848 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
849 if (!l_sysCfgJsonObj.contains("frus"))
850 {
851 logging::logMessage("Missing frus tag in system config JSON");
852 }
853
854 const nlohmann::json& l_listOfFrus =
855 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
856
857 for (const auto& l_frus : l_listOfFrus.items())
858 {
859 for (const auto& l_aFru : l_frus.value())
860 {
861 if (l_aFru["extraInterfaces"].contains(
862 constants::locationCodeInf) &&
863 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
864 "LocationCode", "") == i_unexpandedLocationCode)
865 {
866 return std::get<std::string>(dbusUtility::readDbusProperty(
867 l_aFru["serviceName"], l_aFru["inventoryPath"],
868 constants::locationCodeInf, "LocationCode"));
869 }
870 }
871 }
872 phosphor::logging::elog<types::DbusInvalidArgument>(
873 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
874 types::InvalidArgument::ARGUMENT_VALUE(
875 i_unexpandedLocationCode.c_str()));
876}
877
878types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
879 const std::string& i_unexpandedLocationCode,
880 [[maybe_unused]] const uint16_t i_nodeNumber)
881{
882 types::ListOfPaths l_inventoryPaths;
883
884 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
885 {
886 phosphor::logging::elog<types::DbusInvalidArgument>(
887 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
888 types::InvalidArgument::ARGUMENT_VALUE(
889 i_unexpandedLocationCode.c_str()));
890 }
891
892 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
893 if (!l_sysCfgJsonObj.contains("frus"))
894 {
895 logging::logMessage("Missing frus tag in system config JSON");
896 }
897
898 const nlohmann::json& l_listOfFrus =
899 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
900
901 for (const auto& l_frus : l_listOfFrus.items())
902 {
903 for (const auto& l_aFru : l_frus.value())
904 {
905 if (l_aFru["extraInterfaces"].contains(
906 constants::locationCodeInf) &&
907 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
908 "LocationCode", "") == i_unexpandedLocationCode)
909 {
910 l_inventoryPaths.push_back(
911 l_aFru.at("inventoryPath")
912 .get_ref<const nlohmann::json::string_t&>());
913 }
914 }
915 }
916
917 if (l_inventoryPaths.empty())
918 {
919 phosphor::logging::elog<types::DbusInvalidArgument>(
920 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
921 types::InvalidArgument::ARGUMENT_VALUE(
922 i_unexpandedLocationCode.c_str()));
923 }
924
925 return l_inventoryPaths;
926}
927
Patrick Williams43fedab2025-02-03 14:28:05 -0500928std::string Manager::getHwPath(
929 const sdbusplus::message::object_path& i_dbusObjPath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500930{
931 // Dummy code to supress unused variable warning. To be removed.
932 logging::logMessage(std::string(i_dbusObjPath));
933
934 return std::string{};
935}
936
937std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
938 const std::string& i_expandedLocationCode)
939{
940 /**
941 * Location code should always start with U and fulfil minimum length
942 * criteria.
943 */
944 if (i_expandedLocationCode[0] != 'U' ||
945 i_expandedLocationCode.length() <
946 constants::EXP_LOCATION_CODE_MIN_LENGTH)
947 {
948 phosphor::logging::elog<types::DbusInvalidArgument>(
949 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
950 types::InvalidArgument::ARGUMENT_VALUE(
951 i_expandedLocationCode.c_str()));
952 }
953
954 std::string l_fcKwd;
955
956 auto l_fcKwdValue = dbusUtility::readDbusProperty(
957 "xyz.openbmc_project.Inventory.Manager",
958 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
959 "com.ibm.ipzvpd.VCEN", "FC");
960
961 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
962 {
963 l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
964 }
965
966 // Get the first part of expanded location code to check for FC or TM.
967 std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
968
969 std::string l_unexpandedLocationCode{};
970 uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
971
972 // Check if this value matches the value of FC keyword.
973 if (l_fcKwd.substr(0, 4) == l_firstKwd)
974 {
975 /**
976 * Period(.) should be there in expanded location code to seggregate
977 * FC, node number and SE values.
978 */
979 size_t l_nodeStartPos = i_expandedLocationCode.find('.');
980 if (l_nodeStartPos == std::string::npos)
981 {
982 phosphor::logging::elog<types::DbusInvalidArgument>(
983 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
984 types::InvalidArgument::ARGUMENT_VALUE(
985 i_expandedLocationCode.c_str()));
986 }
987
988 size_t l_nodeEndPos =
989 i_expandedLocationCode.find('.', l_nodeStartPos + 1);
990 if (l_nodeEndPos == std::string::npos)
991 {
992 phosphor::logging::elog<types::DbusInvalidArgument>(
993 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
994 types::InvalidArgument::ARGUMENT_VALUE(
995 i_expandedLocationCode.c_str()));
996 }
997
998 // Skip 3 bytes for '.ND'
999 l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
1000 l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
1001
1002 /**
1003 * Confirm if there are other details apart FC, node number and SE
1004 * in location code
1005 */
1006 if (i_expandedLocationCode.length() >
1007 constants::EXP_LOCATION_CODE_MIN_LENGTH)
1008 {
1009 l_unexpandedLocationCode =
1010 i_expandedLocationCode[0] + std::string("fcs") +
1011 i_expandedLocationCode.substr(
1012 l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
1013 std::string::npos);
1014 }
1015 else
1016 {
1017 l_unexpandedLocationCode = "Ufcs";
1018 }
1019 }
1020 else
1021 {
1022 std::string l_tmKwd;
1023 // Read TM keyword value.
1024 auto l_tmKwdValue = dbusUtility::readDbusProperty(
1025 "xyz.openbmc_project.Inventory.Manager",
1026 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
1027 "com.ibm.ipzvpd.VSYS", "TM");
1028
1029 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
1030 {
1031 l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
1032 }
1033
1034 // Check if the substr matches to TM keyword value.
1035 if (l_tmKwd.substr(0, 4) == l_firstKwd)
1036 {
1037 /**
1038 * System location code will not have node number and any other
1039 * details.
1040 */
1041 l_unexpandedLocationCode = "Umts";
1042 }
1043 // The given location code is neither "fcs" or "mts".
1044 else
1045 {
1046 phosphor::logging::elog<types::DbusInvalidArgument>(
1047 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
1048 types::InvalidArgument::ARGUMENT_VALUE(
1049 i_expandedLocationCode.c_str()));
1050 }
1051 }
1052
1053 return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
1054}
1055
1056types::ListOfPaths Manager::getFrusByExpandedLocationCode(
1057 const std::string& i_expandedLocationCode)
1058{
1059 std::tuple<std::string, uint16_t> l_locationAndNodePair =
1060 getUnexpandedLocationCode(i_expandedLocationCode);
1061
1062 return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
1063 std::get<1>(l_locationAndNodePair));
1064}
1065
1066void Manager::registerHostStateChangeCallback()
1067{
1068 static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
1069 std::make_shared<sdbusplus::bus::match_t>(
1070 *m_asioConnection,
1071 sdbusplus::bus::match::rules::propertiesChanged(
1072 constants::hostObjectPath, constants::hostInterface),
1073 [this](sdbusplus::message_t& i_msg) {
1074 hostStateChangeCallBack(i_msg);
1075 });
1076}
1077
1078void Manager::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
1079{
1080 try
1081 {
1082 if (i_msg.is_method_error())
1083 {
1084 throw std::runtime_error(
1085 "Error reading callback message for host state");
1086 }
1087
1088 std::string l_objectPath;
1089 types::PropertyMap l_propMap;
1090 i_msg.read(l_objectPath, l_propMap);
1091
1092 const auto l_itr = l_propMap.find("CurrentHostState");
1093
1094 if (l_itr == l_propMap.end())
1095 {
1096 throw std::runtime_error(
1097 "CurrentHostState field is missing in callback message");
1098 }
1099
1100 if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
1101 {
1102 // implies system is moving from standby to power on state
1103 if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
1104 "TransitioningToRunning")
1105 {
1106 // TODO: check for all the essential FRUs in the system.
1107
1108 // Perform recollection.
1109 performVpdRecollection();
1110 return;
1111 }
1112 }
1113 else
1114 {
1115 throw std::runtime_error(
1116 "Invalid type recieved in variant for host state.");
1117 }
1118 }
1119 catch (const std::exception& l_ex)
1120 {
1121 // TODO: Log PEL.
1122 logging::logMessage(l_ex.what());
1123 }
1124}
1125
1126void Manager::performVpdRecollection()
1127{
1128 try
1129 {
1130 if (m_worker.get() != nullptr)
1131 {
1132 nlohmann::json l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
1133
1134 // Check if system config JSON is present
1135 if (l_sysCfgJsonObj.empty())
1136 {
1137 throw std::runtime_error(
1138 "System config json object is empty, can't process recollection.");
1139 }
1140
1141 const auto& l_frusReplaceableAtStandby =
1142 jsonUtility::getListOfFrusReplaceableAtStandby(l_sysCfgJsonObj);
1143
1144 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1145 {
1146 // ToDo: Add some logic/trace to know the flow to
1147 // collectSingleFruVpd has been directed via
1148 // performVpdRecollection.
1149 collectSingleFruVpd(l_fruInventoryPath);
1150 }
1151 return;
1152 }
1153
1154 throw std::runtime_error(
1155 "Worker object not found can't process recollection");
1156 }
1157 catch (const std::exception& l_ex)
1158 {
1159 // TODO Log PEL
1160 logging::logMessage(
1161 "VPD recollection failed with error: " + std::string(l_ex.what()));
1162 }
1163}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001164} // namespace vpd