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