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