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