blob: 37fe02bcdfdbc980ab45939088bced9aca13f761 [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(
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530171 "Manager class instantiation failed. " + std::string(e.what()));
172
173 vpd::EventLogger::createSyncPel(
174 vpd::EventLogger::getErrorType(e), vpd::types::SeverityType::Error,
175 __FILE__, __FUNCTION__, 0, vpd::EventLogger::getErrorMsg(e),
176 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500177 }
178}
179
180#ifdef IBM_SYSTEM
181void Manager::registerAssetTagChangeCallback()
182{
183 static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
184 std::make_shared<sdbusplus::bus::match_t>(
185 *m_asioConnection,
186 sdbusplus::bus::match::rules::propertiesChanged(
187 constants::systemInvPath, constants::assetTagInf),
188 [this](sdbusplus::message_t& l_msg) {
189 processAssetTagChangeCallback(l_msg);
190 });
191}
192
193void Manager::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
194{
195 try
196 {
197 if (i_msg.is_method_error())
198 {
199 throw std::runtime_error(
200 "Error reading callback msg for asset tag.");
201 }
202
203 std::string l_objectPath;
204 types::PropertyMap l_propMap;
205 i_msg.read(l_objectPath, l_propMap);
206
207 const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
208 if (l_itrToAssetTag != l_propMap.end())
209 {
210 if (auto l_assetTag =
211 std::get_if<std::string>(&(l_itrToAssetTag->second)))
212 {
213 // Call Notify to persist the AssetTag
214 types::ObjectMap l_objectMap = {
215 {sdbusplus::message::object_path(constants::systemInvPath),
216 {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
217
218 // Notify PIM
219 if (!dbusUtility::callPIM(move(l_objectMap)))
220 {
221 throw std::runtime_error(
222 "Call to PIM failed for asset tag update.");
223 }
224 }
225 }
226 else
227 {
228 throw std::runtime_error(
229 "Could not find asset tag in callback message.");
230 }
231 }
232 catch (const std::exception& l_ex)
233 {
234 // TODO: Log PEL with below description.
235 logging::logMessage("Asset tag callback update failed with error: " +
236 std::string(l_ex.what()));
237 }
238}
239
240void Manager::SetTimerToDetectSVPDOnDbus()
241{
Sunny Srivastava58057c12025-03-19 10:41:34 +0530242 try
243 {
244 static boost::asio::steady_timer timer(*m_ioContext);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500245
Sunny Srivastava58057c12025-03-19 10:41:34 +0530246 // timer for 2 seconds
247 auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500248
Sunny Srivastava58057c12025-03-19 10:41:34 +0530249 (asyncCancelled == 0) ? logging::logMessage("Timer started")
250 : logging::logMessage("Timer re-started");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500251
Sunny Srivastava58057c12025-03-19 10:41:34 +0530252 timer.async_wait([this](const boost::system::error_code& ec) {
253 if (ec == boost::asio::error::operation_aborted)
254 {
255 throw std::runtime_error(
256 std::string(__FUNCTION__) +
257 ": Timer to detect system VPD collection status was aborted.");
258 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500259
Sunny Srivastava58057c12025-03-19 10:41:34 +0530260 if (ec)
261 {
262 throw std::runtime_error(
263 std::string(__FUNCTION__) +
264 ": Timer to detect System VPD collection failed");
265 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500266
Sunny Srivastava58057c12025-03-19 10:41:34 +0530267 if (m_worker->isSystemVPDOnDBus())
268 {
269 // cancel the timer
270 timer.cancel();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500271
Sunny Srivastava58057c12025-03-19 10:41:34 +0530272 // Triggering FRU VPD collection. Setting status to "In
273 // Progress".
274 m_interface->set_property("CollectionStatus",
275 std::string("InProgress"));
276 m_worker->collectFrusFromJson();
277 }
278 });
279 }
280 catch (const std::exception& l_ex)
281 {
282 EventLogger::createAsyncPel(
283 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
284 __FILE__, __FUNCTION__, 0,
285 std::string("Collection for FRUs failed with reason:") +
286 EventLogger::getErrorMsg(l_ex),
287 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
288 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500289}
290
291void Manager::SetTimerToDetectVpdCollectionStatus()
292{
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600293 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500294 // system type.
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600295 static constexpr auto MAX_RETRY = 12;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500296
297 static boost::asio::steady_timer l_timer(*m_ioContext);
298 static uint8_t l_timerRetry = 0;
299
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600300 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500301
302 (l_asyncCancelled == 0)
303 ? logging::logMessage("Collection Timer started")
304 : logging::logMessage("Collection Timer re-started");
305
306 l_timer.async_wait([this](const boost::system::error_code& ec) {
307 if (ec == boost::asio::error::operation_aborted)
308 {
309 throw std::runtime_error(
310 "Timer to detect thread collection status was aborted");
311 }
312
313 if (ec)
314 {
315 throw std::runtime_error(
316 "Timer to detect thread collection failed");
317 }
318
319 if (m_worker->isAllFruCollectionDone())
320 {
321 // cancel the timer
322 l_timer.cancel();
Souvik Roy1f4c8f82025-01-23 00:37:43 -0600323 processFailedEeproms();
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530324
325 // update VPD for powerVS system.
326 ConfigurePowerVsSystem();
327
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500328 m_interface->set_property("CollectionStatus",
329 std::string("Completed"));
330
331 const nlohmann::json& l_sysCfgJsonObj =
332 m_worker->getSysCfgJsonObj();
333 if (jsonUtility::isBackupAndRestoreRequired(l_sysCfgJsonObj))
334 {
335 BackupAndRestore l_backupAndRestoreObj(l_sysCfgJsonObj);
336 l_backupAndRestoreObj.backupAndRestore();
337 }
338 }
339 else
340 {
341 auto l_threadCount = m_worker->getActiveThreadCount();
342 if (l_timerRetry == MAX_RETRY)
343 {
344 l_timer.cancel();
345 logging::logMessage("Taking too long. Active thread = " +
346 std::to_string(l_threadCount));
347 }
348 else
349 {
350 l_timerRetry++;
Sunny Srivastava59f91a82025-02-12 13:19:04 -0600351 logging::logMessage("Collection is in progress for [" +
352 std::to_string(l_threadCount) + "] FRUs.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500353
354 SetTimerToDetectVpdCollectionStatus();
355 }
356 }
357 });
358}
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530359
Sunny Srivastava022112b2025-02-19 19:53:29 +0530360void Manager::checkAndUpdatePowerVsVpd(
361 const nlohmann::json& i_powerVsJsonObj,
362 std::vector<std::string>& o_failedPathList)
363{
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530364 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
365 {
Sunny Srivastavaf1dda762025-02-19 23:46:19 +0530366 nlohmann::json l_sysCfgJsonObj{};
367 if (m_worker.get() != nullptr)
368 {
369 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
370 }
371
372 // The utility method will handle emty JSON case. No explicit
373 // handling required here.
374 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
375 l_sysCfgJsonObj, l_fruPath);
376
377 // Mark it as failed if inventory path not found in JSON.
378 if (l_inventoryPath.empty())
379 {
380 o_failedPathList.push_back(l_fruPath);
381 continue;
382 }
383
384 // check if the FRU is present
385 if (!dbusUtility::isInventoryPresent(l_inventoryPath))
386 {
387 logging::logMessage(
388 "Inventory not present, skip updating part number. Path: " +
389 l_inventoryPath);
390 continue;
391 }
392
393 // check if the FRU needs CCIN check before updating PN.
394 if (l_recJson.contains("CCIN"))
395 {
396 const auto& l_ccinFromDbus =
397 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath);
398
399 // Not an ideal situation as CCIN can't be empty.
400 if (l_ccinFromDbus.empty())
401 {
402 o_failedPathList.push_back(l_fruPath);
403 continue;
404 }
405
406 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
407
408 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
409 l_ccinFromDbus) == l_ccinListFromJson.end())
410 {
411 // Don't update PN in this case.
412 continue;
413 }
414 }
415
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530416 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
417 {
Sunny Srivastavaf1dda762025-02-19 23:46:19 +0530418 // Record name can't be CCIN, skip processing as it is there for PN
419 // update based on CCIN check.
420 if (l_recordName == constants::kwdCCIN)
421 {
422 continue;
423 }
424
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530425 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
426 {
427 // Is value of type array.
428 if (!l_kwdValue.is_array())
429 {
430 o_failedPathList.push_back(l_fruPath);
431 continue;
432 }
433
Sunny Srivastava22793832025-03-20 12:09:09 +0530434 // Get current FRU Part number.
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530435 auto l_retVal = dbusUtility::readDbusProperty(
436 constants::pimServiceName, l_inventoryPath,
Sunny Srivastava22793832025-03-20 12:09:09 +0530437 constants::viniInf, constants::kwdFN);
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530438
Sunny Srivastava22793832025-03-20 12:09:09 +0530439 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530440
Sunny Srivastava22793832025-03-20 12:09:09 +0530441 if (!l_ptrToFn)
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530442 {
443 o_failedPathList.push_back(l_fruPath);
444 continue;
445 }
446
447 types::BinaryVector l_binaryKwdValue =
448 l_kwdValue.get<types::BinaryVector>();
Sunny Srivastava22793832025-03-20 12:09:09 +0530449 if (l_binaryKwdValue == (*l_ptrToFn))
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530450 {
451 continue;
452 }
453
454 // Update part number only if required.
455 if (updateKeyword(
456 l_fruPath,
457 std::make_tuple(l_recordName, l_kwdName, l_kwdValue)) ==
458 constants::FAILURE)
459 {
460 o_failedPathList.push_back(l_fruPath);
461 continue;
462 }
463
Sunny Srivastava22793832025-03-20 12:09:09 +0530464 // update the Asset interface Spare part number explicitly.
Sunny Srivastavab83cb352025-03-06 14:41:27 +0530465 if (!dbusUtility::callPIM(types::ObjectMap{
466 {l_inventoryPath,
Sunny Srivastava22793832025-03-20 12:09:09 +0530467 {{constants::assetInf,
468 {{"SparePartNumber",
Sunny Srivastavab83cb352025-03-06 14:41:27 +0530469 std::string(l_binaryKwdValue.begin(),
470 l_binaryKwdValue.end())}}}}}}))
471 {
472 logging::logMessage(
Sunny Srivastava22793832025-03-20 12:09:09 +0530473 "Updating Spare Part Number under Asset interface failed for path [" +
Sunny Srivastavab83cb352025-03-06 14:41:27 +0530474 l_inventoryPath + "]");
475 }
476
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530477 // Just needed for logging.
Sunny Srivastava22793832025-03-20 12:09:09 +0530478 std::string l_initialPartNum((*l_ptrToFn).begin(),
479 (*l_ptrToFn).end());
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530480 std::string l_finalPartNum(l_binaryKwdValue.begin(),
481 l_binaryKwdValue.end());
482 logging::logMessage(
Sunny Srivastava22793832025-03-20 12:09:09 +0530483 "FRU Part number updated for path [" + l_inventoryPath +
484 "]" + "From [" + l_initialPartNum + "]" + " to [" +
Sunny Srivastava0cd15e12025-02-20 00:01:02 +0530485 l_finalPartNum + "]");
486 }
487 }
488 }
Sunny Srivastava022112b2025-02-19 19:53:29 +0530489}
490
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530491void Manager::ConfigurePowerVsSystem()
492{
Sunny Srivastava022112b2025-02-19 19:53:29 +0530493 std::vector<std::string> l_failedPathList;
Sunny Srivastava1a48f0c2025-02-19 19:02:03 +0530494 try
495 {
496 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
497 if (l_imValue.empty())
498 {
499 throw DbusException("Invalid IM value read from Dbus");
500 }
Sunny Srivastavac6ef42d2025-02-19 19:17:10 +0530501
502 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
503 {
504 // TODO: Should booting be blocked in case of some
505 // misconfigurations?
506 return;
507 }
Sunny Srivastava022112b2025-02-19 19:53:29 +0530508
509 const nlohmann::json& l_powerVsJsonObj =
510 jsonUtility::getPowerVsJson(l_imValue);
511
512 if (l_powerVsJsonObj.empty())
513 {
514 throw std::runtime_error("PowerVS Json not found");
515 }
516
517 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
518
519 if (!l_failedPathList.empty())
520 {
521 throw std::runtime_error(
522 "Part number update failed for following paths: ");
523 }
Sunny Srivastava1a48f0c2025-02-19 19:02:03 +0530524 }
525 catch (const std::exception& l_ex)
526 {
527 // TODO log appropriate PEL
528 }
Sunny Srivastava4c7798a2025-02-19 18:50:34 +0530529}
Sunny Srivastava46f29812025-02-25 11:36:17 +0530530
531void Manager::processFailedEeproms()
532{
533 if (m_worker.get() != nullptr)
534 {
535 // TODO:
536 // - iterate through list of EEPROMs for which thread creation has
537 // failed
538 // - For each failed EEPROM, trigger VPD collection
539 m_worker->getFailedEepromPaths().clear();
540 }
541}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500542#endif
543
544int Manager::updateKeyword(const types::Path i_vpdPath,
545 const types::WriteVpdParams i_paramsToWriteData)
546{
547 if (i_vpdPath.empty())
548 {
549 logging::logMessage("Given VPD path is empty.");
550 return -1;
551 }
552
553 types::Path l_fruPath;
554 nlohmann::json l_sysCfgJsonObj{};
555
556 if (m_worker.get() != nullptr)
557 {
558 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
559
560 // Get the EEPROM path
561 if (!l_sysCfgJsonObj.empty())
562 {
RekhaAparna017fea9f52025-02-17 04:14:02 -0600563 l_fruPath =
564 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500565 }
566 }
567
568 if (l_fruPath.empty())
569 {
570 l_fruPath = i_vpdPath;
571 }
572
573 try
574 {
575 std::shared_ptr<Parser> l_parserObj =
576 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
577 return l_parserObj->updateVpdKeyword(i_paramsToWriteData);
578 }
579 catch (const std::exception& l_exception)
580 {
581 // TODO:: error log needed
582 logging::logMessage("Update keyword failed for file[" + i_vpdPath +
583 "], reason: " + std::string(l_exception.what()));
584 return -1;
585 }
586}
587
588int Manager::updateKeywordOnHardware(
589 const types::Path i_fruPath,
590 const types::WriteVpdParams i_paramsToWriteData) noexcept
591{
592 try
593 {
594 if (i_fruPath.empty())
595 {
596 throw std::runtime_error("Given FRU path is empty");
597 }
598
599 nlohmann::json l_sysCfgJsonObj{};
600
601 if (m_worker.get() != nullptr)
602 {
603 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
604 }
605
606 std::shared_ptr<Parser> l_parserObj =
607 std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
608 return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
609 }
610 catch (const std::exception& l_exception)
611 {
612 EventLogger::createAsyncPel(
613 types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
614 __FILE__, __FUNCTION__, 0,
615 "Update keyword on hardware failed for file[" + i_fruPath +
616 "], reason: " + std::string(l_exception.what()),
617 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
618
619 return constants::FAILURE;
620 }
621}
622
623types::DbusVariantType Manager::readKeyword(
624 const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
625{
626 try
627 {
628 nlohmann::json l_jsonObj{};
629
630 if (m_worker.get() != nullptr)
631 {
632 l_jsonObj = m_worker->getSysCfgJsonObj();
633 }
634
635 std::error_code ec;
636
637 // Check if given path is filesystem path
638 if (!std::filesystem::exists(i_fruPath, ec) && (ec))
639 {
640 throw std::runtime_error(
641 "Given file path " + i_fruPath + " not found.");
642 }
643
644 logging::logMessage("Performing VPD read on " + i_fruPath);
645
646 std::shared_ptr<vpd::Parser> l_parserObj =
647 std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
648
649 std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
650 l_parserObj->getVpdParserInstance();
651
652 return (
653 l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
654 }
655 catch (const std::exception& e)
656 {
657 logging::logMessage(
658 e.what() + std::string(". VPD manager read operation failed for ") +
659 i_fruPath);
660 throw types::DeviceError::ReadFailure();
661 }
662}
663
664void Manager::collectSingleFruVpd(
665 const sdbusplus::message::object_path& i_dbusObjPath)
666{
667 try
668 {
669 if (m_vpdCollectionStatus != "Completed")
670 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600671 logging::logMessage(
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500672 "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
673 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600674 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500675 }
676
677 // Get system config JSON object from worker class
678 nlohmann::json l_sysCfgJsonObj{};
679
680 if (m_worker.get() != nullptr)
681 {
682 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
683 }
684
685 // Check if system config JSON is present
686 if (l_sysCfgJsonObj.empty())
687 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600688 logging::logMessage(
689 "System config JSON object not present. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500690 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600691 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500692 }
693
694 // Get FRU path for the given D-bus object path from JSON
695 const std::string& l_fruPath =
696 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_dbusObjPath);
697
698 if (l_fruPath.empty())
699 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600700 logging::logMessage(
701 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500702 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600703 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500704 }
705
706 // Check if host is up and running
707 if (dbusUtility::isHostRunning())
708 {
709 if (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
710 l_fruPath))
711 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600712 logging::logMessage(
713 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500714 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600715 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500716 }
717 }
718 else if (dbusUtility::isBMCReady())
719 {
720 if (!jsonUtility::isFruReplaceableAtStandby(l_sysCfgJsonObj,
721 l_fruPath) &&
722 (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
723 l_fruPath)))
724 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600725 logging::logMessage(
726 "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 -0500727 std::string(i_dbusObjPath));
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600728 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500729 }
730 }
731
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600732 // Set CollectionStatus as InProgress. Since it's an intermediate state
733 // D-bus set-property call is good enough to update the status.
RekhaAparna01c532b182025-02-19 19:56:49 -0600734 const std::string& l_collStatusProp = "CollectionStatus";
735
736 if (!dbusUtility::writeDbusProperty(
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600737 jsonUtility::getServiceName(l_sysCfgJsonObj,
738 std::string(i_dbusObjPath)),
739 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
740 l_collStatusProp,
RekhaAparna01c532b182025-02-19 19:56:49 -0600741 types::DbusVariantType{constants::vpdCollectionInProgress}))
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600742 {
743 logging::logMessage(
744 "Unable to set CollectionStatus as InProgress for " +
745 std::string(i_dbusObjPath) +
746 ". Continue single FRU VPD collection.");
747 }
748
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500749 // Parse VPD
750 types::VPDMapVariant l_parsedVpd = m_worker->parseVpdFile(l_fruPath);
751
752 // If l_parsedVpd is pointing to std::monostate
753 if (l_parsedVpd.index() == 0)
754 {
755 throw std::runtime_error(
756 "VPD parsing failed for " + std::string(i_dbusObjPath));
757 }
758
759 // Get D-bus object map from worker class
760 types::ObjectMap l_dbusObjectMap;
761 m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
762
763 if (l_dbusObjectMap.empty())
764 {
765 throw std::runtime_error(
766 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
767 std::string(i_dbusObjPath));
768 }
769
770 // Call PIM's Notify method
771 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
772 {
773 throw std::runtime_error(
774 "Notify PIM failed. Single FRU VPD collection failed for " +
775 std::string(i_dbusObjPath));
776 }
777 }
778 catch (const std::exception& l_error)
779 {
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600780 // Notify FRU's VPD CollectionStatus as Failure
781 if (!dbusUtility::notifyFRUCollectionStatus(
782 std::string(i_dbusObjPath), constants::vpdCollectionFailure))
783 {
784 logging::logMessage(
785 "Call to PIM Notify method failed to update Collection status as Failure for " +
786 std::string(i_dbusObjPath));
787 }
788
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500789 // TODO: Log PEL
790 logging::logMessage(std::string(l_error.what()));
791 }
792}
793
794void Manager::deleteSingleFruVpd(
795 const sdbusplus::message::object_path& i_dbusObjPath)
796{
797 try
798 {
799 if (std::string(i_dbusObjPath).empty())
800 {
801 throw std::runtime_error(
802 "Given DBus object path is empty. Aborting FRU VPD deletion.");
803 }
804
805 if (m_worker.get() == nullptr)
806 {
807 throw std::runtime_error(
808 "Worker object not found, can't perform FRU VPD deletion for: " +
809 std::string(i_dbusObjPath));
810 }
811
812 m_worker->deleteFruVpd(std::string(i_dbusObjPath));
813 }
814 catch (const std::exception& l_ex)
815 {
816 // TODO: Log PEL
817 logging::logMessage(l_ex.what());
818 }
819}
820
821bool Manager::isValidUnexpandedLocationCode(
822 const std::string& i_unexpandedLocationCode)
823{
824 if ((i_unexpandedLocationCode.length() <
825 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
826 ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
827 constants::STR_CMP_SUCCESS) &&
828 (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
829 constants::STR_CMP_SUCCESS)) ||
830 ((i_unexpandedLocationCode.length() >
831 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
832 (i_unexpandedLocationCode.find("-") != 4)))
833 {
834 return false;
835 }
836
837 return true;
838}
839
840std::string Manager::getExpandedLocationCode(
841 const std::string& i_unexpandedLocationCode,
842 [[maybe_unused]] const uint16_t i_nodeNumber)
843{
844 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
845 {
846 phosphor::logging::elog<types::DbusInvalidArgument>(
847 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
848 types::InvalidArgument::ARGUMENT_VALUE(
849 i_unexpandedLocationCode.c_str()));
850 }
851
852 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
853 if (!l_sysCfgJsonObj.contains("frus"))
854 {
855 logging::logMessage("Missing frus tag in system config JSON");
856 }
857
858 const nlohmann::json& l_listOfFrus =
859 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
860
861 for (const auto& l_frus : l_listOfFrus.items())
862 {
863 for (const auto& l_aFru : l_frus.value())
864 {
865 if (l_aFru["extraInterfaces"].contains(
866 constants::locationCodeInf) &&
867 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
868 "LocationCode", "") == i_unexpandedLocationCode)
869 {
870 return std::get<std::string>(dbusUtility::readDbusProperty(
871 l_aFru["serviceName"], l_aFru["inventoryPath"],
872 constants::locationCodeInf, "LocationCode"));
873 }
874 }
875 }
876 phosphor::logging::elog<types::DbusInvalidArgument>(
877 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
878 types::InvalidArgument::ARGUMENT_VALUE(
879 i_unexpandedLocationCode.c_str()));
880}
881
882types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
883 const std::string& i_unexpandedLocationCode,
884 [[maybe_unused]] const uint16_t i_nodeNumber)
885{
886 types::ListOfPaths l_inventoryPaths;
887
888 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
889 {
890 phosphor::logging::elog<types::DbusInvalidArgument>(
891 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
892 types::InvalidArgument::ARGUMENT_VALUE(
893 i_unexpandedLocationCode.c_str()));
894 }
895
896 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
897 if (!l_sysCfgJsonObj.contains("frus"))
898 {
899 logging::logMessage("Missing frus tag in system config JSON");
900 }
901
902 const nlohmann::json& l_listOfFrus =
903 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
904
905 for (const auto& l_frus : l_listOfFrus.items())
906 {
907 for (const auto& l_aFru : l_frus.value())
908 {
909 if (l_aFru["extraInterfaces"].contains(
910 constants::locationCodeInf) &&
911 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
912 "LocationCode", "") == i_unexpandedLocationCode)
913 {
914 l_inventoryPaths.push_back(
915 l_aFru.at("inventoryPath")
916 .get_ref<const nlohmann::json::string_t&>());
917 }
918 }
919 }
920
921 if (l_inventoryPaths.empty())
922 {
923 phosphor::logging::elog<types::DbusInvalidArgument>(
924 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
925 types::InvalidArgument::ARGUMENT_VALUE(
926 i_unexpandedLocationCode.c_str()));
927 }
928
929 return l_inventoryPaths;
930}
931
Patrick Williams43fedab2025-02-03 14:28:05 -0500932std::string Manager::getHwPath(
933 const sdbusplus::message::object_path& i_dbusObjPath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500934{
935 // Dummy code to supress unused variable warning. To be removed.
936 logging::logMessage(std::string(i_dbusObjPath));
937
938 return std::string{};
939}
940
941std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
942 const std::string& i_expandedLocationCode)
943{
944 /**
945 * Location code should always start with U and fulfil minimum length
946 * criteria.
947 */
948 if (i_expandedLocationCode[0] != 'U' ||
949 i_expandedLocationCode.length() <
950 constants::EXP_LOCATION_CODE_MIN_LENGTH)
951 {
952 phosphor::logging::elog<types::DbusInvalidArgument>(
953 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
954 types::InvalidArgument::ARGUMENT_VALUE(
955 i_expandedLocationCode.c_str()));
956 }
957
958 std::string l_fcKwd;
959
960 auto l_fcKwdValue = dbusUtility::readDbusProperty(
961 "xyz.openbmc_project.Inventory.Manager",
962 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
963 "com.ibm.ipzvpd.VCEN", "FC");
964
965 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
966 {
967 l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
968 }
969
970 // Get the first part of expanded location code to check for FC or TM.
971 std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
972
973 std::string l_unexpandedLocationCode{};
974 uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
975
976 // Check if this value matches the value of FC keyword.
977 if (l_fcKwd.substr(0, 4) == l_firstKwd)
978 {
979 /**
980 * Period(.) should be there in expanded location code to seggregate
981 * FC, node number and SE values.
982 */
983 size_t l_nodeStartPos = i_expandedLocationCode.find('.');
984 if (l_nodeStartPos == std::string::npos)
985 {
986 phosphor::logging::elog<types::DbusInvalidArgument>(
987 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
988 types::InvalidArgument::ARGUMENT_VALUE(
989 i_expandedLocationCode.c_str()));
990 }
991
992 size_t l_nodeEndPos =
993 i_expandedLocationCode.find('.', l_nodeStartPos + 1);
994 if (l_nodeEndPos == std::string::npos)
995 {
996 phosphor::logging::elog<types::DbusInvalidArgument>(
997 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
998 types::InvalidArgument::ARGUMENT_VALUE(
999 i_expandedLocationCode.c_str()));
1000 }
1001
1002 // Skip 3 bytes for '.ND'
1003 l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
1004 l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
1005
1006 /**
1007 * Confirm if there are other details apart FC, node number and SE
1008 * in location code
1009 */
1010 if (i_expandedLocationCode.length() >
1011 constants::EXP_LOCATION_CODE_MIN_LENGTH)
1012 {
1013 l_unexpandedLocationCode =
1014 i_expandedLocationCode[0] + std::string("fcs") +
1015 i_expandedLocationCode.substr(
1016 l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
1017 std::string::npos);
1018 }
1019 else
1020 {
1021 l_unexpandedLocationCode = "Ufcs";
1022 }
1023 }
1024 else
1025 {
1026 std::string l_tmKwd;
1027 // Read TM keyword value.
1028 auto l_tmKwdValue = dbusUtility::readDbusProperty(
1029 "xyz.openbmc_project.Inventory.Manager",
1030 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
1031 "com.ibm.ipzvpd.VSYS", "TM");
1032
1033 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
1034 {
1035 l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
1036 }
1037
1038 // Check if the substr matches to TM keyword value.
1039 if (l_tmKwd.substr(0, 4) == l_firstKwd)
1040 {
1041 /**
1042 * System location code will not have node number and any other
1043 * details.
1044 */
1045 l_unexpandedLocationCode = "Umts";
1046 }
1047 // The given location code is neither "fcs" or "mts".
1048 else
1049 {
1050 phosphor::logging::elog<types::DbusInvalidArgument>(
1051 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
1052 types::InvalidArgument::ARGUMENT_VALUE(
1053 i_expandedLocationCode.c_str()));
1054 }
1055 }
1056
1057 return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
1058}
1059
1060types::ListOfPaths Manager::getFrusByExpandedLocationCode(
1061 const std::string& i_expandedLocationCode)
1062{
1063 std::tuple<std::string, uint16_t> l_locationAndNodePair =
1064 getUnexpandedLocationCode(i_expandedLocationCode);
1065
1066 return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
1067 std::get<1>(l_locationAndNodePair));
1068}
1069
1070void Manager::registerHostStateChangeCallback()
1071{
1072 static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
1073 std::make_shared<sdbusplus::bus::match_t>(
1074 *m_asioConnection,
1075 sdbusplus::bus::match::rules::propertiesChanged(
1076 constants::hostObjectPath, constants::hostInterface),
1077 [this](sdbusplus::message_t& i_msg) {
1078 hostStateChangeCallBack(i_msg);
1079 });
1080}
1081
1082void Manager::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
1083{
1084 try
1085 {
1086 if (i_msg.is_method_error())
1087 {
1088 throw std::runtime_error(
1089 "Error reading callback message for host state");
1090 }
1091
1092 std::string l_objectPath;
1093 types::PropertyMap l_propMap;
1094 i_msg.read(l_objectPath, l_propMap);
1095
1096 const auto l_itr = l_propMap.find("CurrentHostState");
1097
1098 if (l_itr == l_propMap.end())
1099 {
1100 throw std::runtime_error(
1101 "CurrentHostState field is missing in callback message");
1102 }
1103
1104 if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
1105 {
1106 // implies system is moving from standby to power on state
1107 if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
1108 "TransitioningToRunning")
1109 {
1110 // TODO: check for all the essential FRUs in the system.
1111
1112 // Perform recollection.
1113 performVpdRecollection();
1114 return;
1115 }
1116 }
1117 else
1118 {
1119 throw std::runtime_error(
1120 "Invalid type recieved in variant for host state.");
1121 }
1122 }
1123 catch (const std::exception& l_ex)
1124 {
1125 // TODO: Log PEL.
1126 logging::logMessage(l_ex.what());
1127 }
1128}
1129
1130void Manager::performVpdRecollection()
1131{
1132 try
1133 {
1134 if (m_worker.get() != nullptr)
1135 {
1136 nlohmann::json l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
1137
1138 // Check if system config JSON is present
1139 if (l_sysCfgJsonObj.empty())
1140 {
1141 throw std::runtime_error(
1142 "System config json object is empty, can't process recollection.");
1143 }
1144
1145 const auto& l_frusReplaceableAtStandby =
1146 jsonUtility::getListOfFrusReplaceableAtStandby(l_sysCfgJsonObj);
1147
1148 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1149 {
1150 // ToDo: Add some logic/trace to know the flow to
1151 // collectSingleFruVpd has been directed via
1152 // performVpdRecollection.
1153 collectSingleFruVpd(l_fruInventoryPath);
1154 }
1155 return;
1156 }
1157
1158 throw std::runtime_error(
1159 "Worker object not found can't process recollection");
1160 }
1161 catch (const std::exception& l_ex)
1162 {
1163 // TODO Log PEL
1164 logging::logMessage(
1165 "VPD recollection failed with error: " + std::string(l_ex.what()));
1166 }
1167}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001168} // namespace vpd