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