blob: 1e3f3d5323367d212a089b8bc11993bca6b30453 [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{
259 // Keeping max retry for 2 minutes. TODO: Make it cinfigurable based on
260 // system type.
261 static constexpr auto MAX_RETRY = 40;
262
263 static boost::asio::steady_timer l_timer(*m_ioContext);
264 static uint8_t l_timerRetry = 0;
265
266 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(3));
267
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 Srivastavafa5e4d32023-03-12 11:59:49 -0500290 m_interface->set_property("CollectionStatus",
291 std::string("Completed"));
292
293 const nlohmann::json& l_sysCfgJsonObj =
294 m_worker->getSysCfgJsonObj();
295 if (jsonUtility::isBackupAndRestoreRequired(l_sysCfgJsonObj))
296 {
297 BackupAndRestore l_backupAndRestoreObj(l_sysCfgJsonObj);
298 l_backupAndRestoreObj.backupAndRestore();
299 }
300 }
301 else
302 {
303 auto l_threadCount = m_worker->getActiveThreadCount();
304 if (l_timerRetry == MAX_RETRY)
305 {
306 l_timer.cancel();
307 logging::logMessage("Taking too long. Active thread = " +
308 std::to_string(l_threadCount));
309 }
310 else
311 {
312 l_timerRetry++;
313 logging::logMessage("Waiting... active thread = " +
314 std::to_string(l_threadCount) + "After " +
315 std::to_string(l_timerRetry) + " re-tries");
316
317 SetTimerToDetectVpdCollectionStatus();
318 }
319 }
320 });
321}
322#endif
323
324int Manager::updateKeyword(const types::Path i_vpdPath,
325 const types::WriteVpdParams i_paramsToWriteData)
326{
327 if (i_vpdPath.empty())
328 {
329 logging::logMessage("Given VPD path is empty.");
330 return -1;
331 }
332
333 types::Path l_fruPath;
334 nlohmann::json l_sysCfgJsonObj{};
335
336 if (m_worker.get() != nullptr)
337 {
338 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
339
340 // Get the EEPROM path
341 if (!l_sysCfgJsonObj.empty())
342 {
343 try
344 {
345 l_fruPath =
346 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
347 }
348 catch (const std::exception& l_exception)
349 {
350 logging::logMessage(
351 "Error while getting FRU path, Path: " + i_vpdPath +
352 ", error: " + std::string(l_exception.what()));
353 return -1;
354 }
355 }
356 }
357
358 if (l_fruPath.empty())
359 {
360 l_fruPath = i_vpdPath;
361 }
362
363 try
364 {
365 std::shared_ptr<Parser> l_parserObj =
366 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
367 return l_parserObj->updateVpdKeyword(i_paramsToWriteData);
368 }
369 catch (const std::exception& l_exception)
370 {
371 // TODO:: error log needed
372 logging::logMessage("Update keyword failed for file[" + i_vpdPath +
373 "], reason: " + std::string(l_exception.what()));
374 return -1;
375 }
376}
377
378int Manager::updateKeywordOnHardware(
379 const types::Path i_fruPath,
380 const types::WriteVpdParams i_paramsToWriteData) noexcept
381{
382 try
383 {
384 if (i_fruPath.empty())
385 {
386 throw std::runtime_error("Given FRU path is empty");
387 }
388
389 nlohmann::json l_sysCfgJsonObj{};
390
391 if (m_worker.get() != nullptr)
392 {
393 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
394 }
395
396 std::shared_ptr<Parser> l_parserObj =
397 std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
398 return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
399 }
400 catch (const std::exception& l_exception)
401 {
402 EventLogger::createAsyncPel(
403 types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
404 __FILE__, __FUNCTION__, 0,
405 "Update keyword on hardware failed for file[" + i_fruPath +
406 "], reason: " + std::string(l_exception.what()),
407 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
408
409 return constants::FAILURE;
410 }
411}
412
413types::DbusVariantType Manager::readKeyword(
414 const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
415{
416 try
417 {
418 nlohmann::json l_jsonObj{};
419
420 if (m_worker.get() != nullptr)
421 {
422 l_jsonObj = m_worker->getSysCfgJsonObj();
423 }
424
425 std::error_code ec;
426
427 // Check if given path is filesystem path
428 if (!std::filesystem::exists(i_fruPath, ec) && (ec))
429 {
430 throw std::runtime_error(
431 "Given file path " + i_fruPath + " not found.");
432 }
433
434 logging::logMessage("Performing VPD read on " + i_fruPath);
435
436 std::shared_ptr<vpd::Parser> l_parserObj =
437 std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
438
439 std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
440 l_parserObj->getVpdParserInstance();
441
442 return (
443 l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
444 }
445 catch (const std::exception& e)
446 {
447 logging::logMessage(
448 e.what() + std::string(". VPD manager read operation failed for ") +
449 i_fruPath);
450 throw types::DeviceError::ReadFailure();
451 }
452}
453
454void Manager::collectSingleFruVpd(
455 const sdbusplus::message::object_path& i_dbusObjPath)
456{
457 try
458 {
459 if (m_vpdCollectionStatus != "Completed")
460 {
461 throw std::runtime_error(
462 "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
463 std::string(i_dbusObjPath));
464 }
465
466 // Get system config JSON object from worker class
467 nlohmann::json l_sysCfgJsonObj{};
468
469 if (m_worker.get() != nullptr)
470 {
471 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
472 }
473
474 // Check if system config JSON is present
475 if (l_sysCfgJsonObj.empty())
476 {
477 throw std::runtime_error(
478 "System config JSON object not present. Single FRU VPD collection failed for " +
479 std::string(i_dbusObjPath));
480 }
481
482 // Get FRU path for the given D-bus object path from JSON
483 const std::string& l_fruPath =
484 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_dbusObjPath);
485
486 if (l_fruPath.empty())
487 {
488 throw std::runtime_error(
489 "D-bus object path not present in JSON. Single FRU VPD collection failed for " +
490 std::string(i_dbusObjPath));
491 }
492
493 // Check if host is up and running
494 if (dbusUtility::isHostRunning())
495 {
496 if (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
497 l_fruPath))
498 {
499 throw std::runtime_error(
500 "Given FRU is not replaceable at host runtime. Single FRU VPD collection failed for " +
501 std::string(i_dbusObjPath));
502 }
503 }
504 else if (dbusUtility::isBMCReady())
505 {
506 if (!jsonUtility::isFruReplaceableAtStandby(l_sysCfgJsonObj,
507 l_fruPath) &&
508 (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
509 l_fruPath)))
510 {
511 throw std::runtime_error(
512 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection failed for " +
513 std::string(i_dbusObjPath));
514 }
515 }
516
517 // Parse VPD
518 types::VPDMapVariant l_parsedVpd = m_worker->parseVpdFile(l_fruPath);
519
520 // If l_parsedVpd is pointing to std::monostate
521 if (l_parsedVpd.index() == 0)
522 {
523 throw std::runtime_error(
524 "VPD parsing failed for " + std::string(i_dbusObjPath));
525 }
526
527 // Get D-bus object map from worker class
528 types::ObjectMap l_dbusObjectMap;
529 m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
530
531 if (l_dbusObjectMap.empty())
532 {
533 throw std::runtime_error(
534 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
535 std::string(i_dbusObjPath));
536 }
537
538 // Call PIM's Notify method
539 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
540 {
541 throw std::runtime_error(
542 "Notify PIM failed. Single FRU VPD collection failed for " +
543 std::string(i_dbusObjPath));
544 }
545 }
546 catch (const std::exception& l_error)
547 {
548 // TODO: Log PEL
549 logging::logMessage(std::string(l_error.what()));
550 }
551}
552
553void Manager::deleteSingleFruVpd(
554 const sdbusplus::message::object_path& i_dbusObjPath)
555{
556 try
557 {
558 if (std::string(i_dbusObjPath).empty())
559 {
560 throw std::runtime_error(
561 "Given DBus object path is empty. Aborting FRU VPD deletion.");
562 }
563
564 if (m_worker.get() == nullptr)
565 {
566 throw std::runtime_error(
567 "Worker object not found, can't perform FRU VPD deletion for: " +
568 std::string(i_dbusObjPath));
569 }
570
571 m_worker->deleteFruVpd(std::string(i_dbusObjPath));
572 }
573 catch (const std::exception& l_ex)
574 {
575 // TODO: Log PEL
576 logging::logMessage(l_ex.what());
577 }
578}
579
580bool Manager::isValidUnexpandedLocationCode(
581 const std::string& i_unexpandedLocationCode)
582{
583 if ((i_unexpandedLocationCode.length() <
584 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
585 ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
586 constants::STR_CMP_SUCCESS) &&
587 (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
588 constants::STR_CMP_SUCCESS)) ||
589 ((i_unexpandedLocationCode.length() >
590 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
591 (i_unexpandedLocationCode.find("-") != 4)))
592 {
593 return false;
594 }
595
596 return true;
597}
598
599std::string Manager::getExpandedLocationCode(
600 const std::string& i_unexpandedLocationCode,
601 [[maybe_unused]] const uint16_t i_nodeNumber)
602{
603 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
604 {
605 phosphor::logging::elog<types::DbusInvalidArgument>(
606 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
607 types::InvalidArgument::ARGUMENT_VALUE(
608 i_unexpandedLocationCode.c_str()));
609 }
610
611 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
612 if (!l_sysCfgJsonObj.contains("frus"))
613 {
614 logging::logMessage("Missing frus tag in system config JSON");
615 }
616
617 const nlohmann::json& l_listOfFrus =
618 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
619
620 for (const auto& l_frus : l_listOfFrus.items())
621 {
622 for (const auto& l_aFru : l_frus.value())
623 {
624 if (l_aFru["extraInterfaces"].contains(
625 constants::locationCodeInf) &&
626 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
627 "LocationCode", "") == i_unexpandedLocationCode)
628 {
629 return std::get<std::string>(dbusUtility::readDbusProperty(
630 l_aFru["serviceName"], l_aFru["inventoryPath"],
631 constants::locationCodeInf, "LocationCode"));
632 }
633 }
634 }
635 phosphor::logging::elog<types::DbusInvalidArgument>(
636 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
637 types::InvalidArgument::ARGUMENT_VALUE(
638 i_unexpandedLocationCode.c_str()));
639}
640
641types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
642 const std::string& i_unexpandedLocationCode,
643 [[maybe_unused]] const uint16_t i_nodeNumber)
644{
645 types::ListOfPaths l_inventoryPaths;
646
647 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
648 {
649 phosphor::logging::elog<types::DbusInvalidArgument>(
650 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
651 types::InvalidArgument::ARGUMENT_VALUE(
652 i_unexpandedLocationCode.c_str()));
653 }
654
655 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
656 if (!l_sysCfgJsonObj.contains("frus"))
657 {
658 logging::logMessage("Missing frus tag in system config JSON");
659 }
660
661 const nlohmann::json& l_listOfFrus =
662 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
663
664 for (const auto& l_frus : l_listOfFrus.items())
665 {
666 for (const auto& l_aFru : l_frus.value())
667 {
668 if (l_aFru["extraInterfaces"].contains(
669 constants::locationCodeInf) &&
670 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
671 "LocationCode", "") == i_unexpandedLocationCode)
672 {
673 l_inventoryPaths.push_back(
674 l_aFru.at("inventoryPath")
675 .get_ref<const nlohmann::json::string_t&>());
676 }
677 }
678 }
679
680 if (l_inventoryPaths.empty())
681 {
682 phosphor::logging::elog<types::DbusInvalidArgument>(
683 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
684 types::InvalidArgument::ARGUMENT_VALUE(
685 i_unexpandedLocationCode.c_str()));
686 }
687
688 return l_inventoryPaths;
689}
690
691std::string
692 Manager::getHwPath(const sdbusplus::message::object_path& i_dbusObjPath)
693{
694 // Dummy code to supress unused variable warning. To be removed.
695 logging::logMessage(std::string(i_dbusObjPath));
696
697 return std::string{};
698}
699
700std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
701 const std::string& i_expandedLocationCode)
702{
703 /**
704 * Location code should always start with U and fulfil minimum length
705 * criteria.
706 */
707 if (i_expandedLocationCode[0] != 'U' ||
708 i_expandedLocationCode.length() <
709 constants::EXP_LOCATION_CODE_MIN_LENGTH)
710 {
711 phosphor::logging::elog<types::DbusInvalidArgument>(
712 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
713 types::InvalidArgument::ARGUMENT_VALUE(
714 i_expandedLocationCode.c_str()));
715 }
716
717 std::string l_fcKwd;
718
719 auto l_fcKwdValue = dbusUtility::readDbusProperty(
720 "xyz.openbmc_project.Inventory.Manager",
721 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
722 "com.ibm.ipzvpd.VCEN", "FC");
723
724 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
725 {
726 l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
727 }
728
729 // Get the first part of expanded location code to check for FC or TM.
730 std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
731
732 std::string l_unexpandedLocationCode{};
733 uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
734
735 // Check if this value matches the value of FC keyword.
736 if (l_fcKwd.substr(0, 4) == l_firstKwd)
737 {
738 /**
739 * Period(.) should be there in expanded location code to seggregate
740 * FC, node number and SE values.
741 */
742 size_t l_nodeStartPos = i_expandedLocationCode.find('.');
743 if (l_nodeStartPos == std::string::npos)
744 {
745 phosphor::logging::elog<types::DbusInvalidArgument>(
746 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
747 types::InvalidArgument::ARGUMENT_VALUE(
748 i_expandedLocationCode.c_str()));
749 }
750
751 size_t l_nodeEndPos =
752 i_expandedLocationCode.find('.', l_nodeStartPos + 1);
753 if (l_nodeEndPos == std::string::npos)
754 {
755 phosphor::logging::elog<types::DbusInvalidArgument>(
756 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
757 types::InvalidArgument::ARGUMENT_VALUE(
758 i_expandedLocationCode.c_str()));
759 }
760
761 // Skip 3 bytes for '.ND'
762 l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
763 l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
764
765 /**
766 * Confirm if there are other details apart FC, node number and SE
767 * in location code
768 */
769 if (i_expandedLocationCode.length() >
770 constants::EXP_LOCATION_CODE_MIN_LENGTH)
771 {
772 l_unexpandedLocationCode =
773 i_expandedLocationCode[0] + std::string("fcs") +
774 i_expandedLocationCode.substr(
775 l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
776 std::string::npos);
777 }
778 else
779 {
780 l_unexpandedLocationCode = "Ufcs";
781 }
782 }
783 else
784 {
785 std::string l_tmKwd;
786 // Read TM keyword value.
787 auto l_tmKwdValue = dbusUtility::readDbusProperty(
788 "xyz.openbmc_project.Inventory.Manager",
789 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
790 "com.ibm.ipzvpd.VSYS", "TM");
791
792 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
793 {
794 l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
795 }
796
797 // Check if the substr matches to TM keyword value.
798 if (l_tmKwd.substr(0, 4) == l_firstKwd)
799 {
800 /**
801 * System location code will not have node number and any other
802 * details.
803 */
804 l_unexpandedLocationCode = "Umts";
805 }
806 // The given location code is neither "fcs" or "mts".
807 else
808 {
809 phosphor::logging::elog<types::DbusInvalidArgument>(
810 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
811 types::InvalidArgument::ARGUMENT_VALUE(
812 i_expandedLocationCode.c_str()));
813 }
814 }
815
816 return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
817}
818
819types::ListOfPaths Manager::getFrusByExpandedLocationCode(
820 const std::string& i_expandedLocationCode)
821{
822 std::tuple<std::string, uint16_t> l_locationAndNodePair =
823 getUnexpandedLocationCode(i_expandedLocationCode);
824
825 return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
826 std::get<1>(l_locationAndNodePair));
827}
828
829void Manager::registerHostStateChangeCallback()
830{
831 static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
832 std::make_shared<sdbusplus::bus::match_t>(
833 *m_asioConnection,
834 sdbusplus::bus::match::rules::propertiesChanged(
835 constants::hostObjectPath, constants::hostInterface),
836 [this](sdbusplus::message_t& i_msg) {
837 hostStateChangeCallBack(i_msg);
838 });
839}
840
841void Manager::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
842{
843 try
844 {
845 if (i_msg.is_method_error())
846 {
847 throw std::runtime_error(
848 "Error reading callback message for host state");
849 }
850
851 std::string l_objectPath;
852 types::PropertyMap l_propMap;
853 i_msg.read(l_objectPath, l_propMap);
854
855 const auto l_itr = l_propMap.find("CurrentHostState");
856
857 if (l_itr == l_propMap.end())
858 {
859 throw std::runtime_error(
860 "CurrentHostState field is missing in callback message");
861 }
862
863 if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
864 {
865 // implies system is moving from standby to power on state
866 if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
867 "TransitioningToRunning")
868 {
869 // TODO: check for all the essential FRUs in the system.
870
871 // Perform recollection.
872 performVpdRecollection();
873 return;
874 }
875 }
876 else
877 {
878 throw std::runtime_error(
879 "Invalid type recieved in variant for host state.");
880 }
881 }
882 catch (const std::exception& l_ex)
883 {
884 // TODO: Log PEL.
885 logging::logMessage(l_ex.what());
886 }
887}
888
889void Manager::performVpdRecollection()
890{
891 try
892 {
893 if (m_worker.get() != nullptr)
894 {
895 nlohmann::json l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
896
897 // Check if system config JSON is present
898 if (l_sysCfgJsonObj.empty())
899 {
900 throw std::runtime_error(
901 "System config json object is empty, can't process recollection.");
902 }
903
904 const auto& l_frusReplaceableAtStandby =
905 jsonUtility::getListOfFrusReplaceableAtStandby(l_sysCfgJsonObj);
906
907 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
908 {
909 // ToDo: Add some logic/trace to know the flow to
910 // collectSingleFruVpd has been directed via
911 // performVpdRecollection.
912 collectSingleFruVpd(l_fruInventoryPath);
913 }
914 return;
915 }
916
917 throw std::runtime_error(
918 "Worker object not found can't process recollection");
919 }
920 catch (const std::exception& l_ex)
921 {
922 // TODO Log PEL
923 logging::logMessage(
924 "VPD recollection failed with error: " + std::string(l_ex.what()));
925 }
926}
Souvik Roy1f4c8f82025-01-23 00:37:43 -0600927
928void Manager::processFailedEeproms()
929{
930 if (m_worker.get() != nullptr)
931 {
932 // TODO:
933 // - iterate through list of EEPROMs for which thread creation has
934 // failed
935 // - For each failed EEPROM, trigger VPD collection
936 m_worker->getFailedEepromPaths().clear();
937 }
938}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500939} // namespace vpd