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