blob: 39a4bf3ceb7ef7cfe87b7ea29437838b72cb3711 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#pragma once
2
3#include "constants.hpp"
Sunny Srivastavac6ef42d2025-02-19 19:17:10 +05304#include "exceptions.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05005#include "logger.hpp"
6#include "types.hpp"
7
8#include <chrono>
9
10namespace vpd
11{
12/**
13 * @brief The namespace defines utlity methods for generic D-Bus operations.
14 */
15namespace dbusUtility
16{
17
18/**
19 * @brief An API to get Map of service and interfaces for an object path.
20 *
21 * The API returns a Map of service name and interfaces for a given pair of
22 * object path and interface list. It can be used to determine service name
23 * which implemets a particular object path and interface.
24 *
25 * Note: It will be caller's responsibility to check for empty map returned and
26 * generate appropriate error.
27 *
28 * @param [in] objectPath - Object path under the service.
29 * @param [in] interfaces - Array of interface(s).
30 * @return - A Map of service name to object to interface(s), if success.
31 * If failed, empty map.
32 */
33inline types::MapperGetObject getObjectMap(const std::string& objectPath,
34 std::span<const char*> interfaces)
35{
36 types::MapperGetObject getObjectMap;
37
38 // interface list is optional argument, hence no check required.
39 if (objectPath.empty())
40 {
41 logging::logMessage("Path value is empty, invalid call to GetObject");
42 return getObjectMap;
43 }
44
45 try
46 {
47 auto bus = sdbusplus::bus::new_default();
48 auto method = bus.new_method_call(
49 "xyz.openbmc_project.ObjectMapper",
50 "/xyz/openbmc_project/object_mapper",
51 "xyz.openbmc_project.ObjectMapper", "GetObject");
52
53 method.append(objectPath, interfaces);
54 auto result = bus.call(method);
55 result.read(getObjectMap);
56 }
57 catch (const sdbusplus::exception::SdBusError& e)
58 {
59 // logging::logMessage(e.what());
60 return getObjectMap;
61 }
62
63 return getObjectMap;
64}
65
66/**
67 * @brief An API to get property map for an interface.
68 *
69 * This API returns a map of property and its value with respect to a particular
70 * interface.
71 *
72 * Note: It will be caller's responsibility to check for empty map returned and
73 * generate appropriate error.
74 *
75 * @param[in] i_service - Service name.
76 * @param[in] i_objectPath - object path.
77 * @param[in] i_interface - Interface, for the properties to be listed.
78 *
79 * @return - A map of property and value of an interface, if success.
80 * if failed, empty map.
81 */
82inline types::PropertyMap getPropertyMap(const std::string& i_service,
83 const std::string& i_objectPath,
84 const std::string& i_interface)
85{
86 types::PropertyMap l_propertyValueMap;
87 if (i_service.empty() || i_objectPath.empty() || i_interface.empty())
88 {
89 logging::logMessage("Invalid parameters to get property map");
90 return l_propertyValueMap;
91 }
92
93 try
94 {
95 auto l_bus = sdbusplus::bus::new_default();
96 auto l_method =
97 l_bus.new_method_call(i_service.c_str(), i_objectPath.c_str(),
98 "org.freedesktop.DBus.Properties", "GetAll");
99 l_method.append(i_interface);
100 auto l_result = l_bus.call(l_method);
101 l_result.read(l_propertyValueMap);
102 }
103 catch (const sdbusplus::exception::SdBusError& l_ex)
104 {
105 logging::logMessage(l_ex.what());
106 }
107
108 return l_propertyValueMap;
109}
110
111/**
112 * @brief API to get object subtree from D-bus.
113 *
114 * The API returns the map of object, services and interfaces in the
115 * subtree that implement a certain interface. If no interfaces are provided
116 * then all the objects, services and interfaces under the subtree will
117 * be returned.
118 *
119 * Note: Depth can be 0 and interfaces can be null.
120 * It will be caller's responsibility to check for empty vector returned
121 * and generate appropriate error.
122 *
123 * @param[in] i_objectPath - Path to search for an interface.
124 * @param[in] i_depth - Maximum depth of the tree to search.
125 * @param[in] i_interfaces - List of interfaces to search.
126 *
127 * @return - A map of object and its related services and interfaces, if
128 * success. If failed, empty map.
129 */
130
Patrick Williams43fedab2025-02-03 14:28:05 -0500131inline types::MapperGetSubTree getObjectSubTree(
132 const std::string& i_objectPath, const int& i_depth,
133 const std::vector<std::string>& i_interfaces)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500134{
135 types::MapperGetSubTree l_subTreeMap;
136
137 if (i_objectPath.empty())
138 {
139 logging::logMessage("Object path is empty.");
140 return l_subTreeMap;
141 }
142
143 try
144 {
145 auto l_bus = sdbusplus::bus::new_default();
146 auto l_method = l_bus.new_method_call(
147 constants::objectMapperService, constants::objectMapperPath,
148 constants::objectMapperInf, "GetSubTree");
149 l_method.append(i_objectPath, i_depth, i_interfaces);
150 auto l_result = l_bus.call(l_method);
151 l_result.read(l_subTreeMap);
152 }
153 catch (const sdbusplus::exception::SdBusError& l_ex)
154 {
155 logging::logMessage(l_ex.what());
156 }
157
158 return l_subTreeMap;
159}
160
161/**
162 * @brief An API to read property from Dbus.
163 *
164 * The caller of the API needs to validate the validatity and correctness of the
165 * type and value of data returned. The API will just fetch and retun the data
166 * without any data validation.
167 *
168 * Note: It will be caller's responsibility to check for empty value returned
169 * and generate appropriate error if required.
170 *
171 * @param [in] serviceName - Name of the Dbus service.
172 * @param [in] objectPath - Object path under the service.
173 * @param [in] interface - Interface under which property exist.
174 * @param [in] property - Property whose value is to be read.
175 * @return - Value read from Dbus, if success.
176 * If failed, empty variant.
177 */
178inline types::DbusVariantType readDbusProperty(
179 const std::string& serviceName, const std::string& objectPath,
180 const std::string& interface, const std::string& property)
181{
182 types::DbusVariantType propertyValue;
183
184 // Mandatory fields to make a read dbus call.
185 if (serviceName.empty() || objectPath.empty() || interface.empty() ||
186 property.empty())
187 {
188 logging::logMessage(
189 "One of the parameter to make Dbus read call is empty.");
190 return propertyValue;
191 }
192
193 try
194 {
195 auto bus = sdbusplus::bus::new_default();
196 auto method =
197 bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
198 "org.freedesktop.DBus.Properties", "Get");
199 method.append(interface, property);
200
201 auto result = bus.call(method);
202 result.read(propertyValue);
203 }
204 catch (const sdbusplus::exception::SdBusError& e)
205 {
206 return propertyValue;
207 }
208 return propertyValue;
209}
210
211/**
212 * @brief An API to write property on Dbus.
213 *
214 * The caller of this API needs to handle exception thrown by this method to
215 * identify any write failure. The API in no other way indicate write failure
216 * to the caller.
217 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500218 * @param [in] serviceName - Name of the Dbus service.
219 * @param [in] objectPath - Object path under the service.
220 * @param [in] interface - Interface under which property exist.
221 * @param [in] property - Property whose value is to be written.
222 * @param [in] propertyValue - The value to be written.
RekhaAparna01c532b182025-02-19 19:56:49 -0600223 * @return True if write on DBus is success, false otherwise.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500224 */
RekhaAparna01c532b182025-02-19 19:56:49 -0600225inline bool writeDbusProperty(
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500226 const std::string& serviceName, const std::string& objectPath,
227 const std::string& interface, const std::string& property,
228 const types::DbusVariantType& propertyValue)
229{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500230 try
231 {
RekhaAparna01c532b182025-02-19 19:56:49 -0600232 // Mandatory fields to make a write dbus call.
233 if (serviceName.empty() || objectPath.empty() || interface.empty() ||
234 property.empty())
235 {
236 throw std::runtime_error("Dbus write failed, Parameter empty");
237 }
238
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500239 auto bus = sdbusplus::bus::new_default();
240 auto method =
241 bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
242 "org.freedesktop.DBus.Properties", "Set");
243 method.append(interface, property, propertyValue);
244 bus.call(method);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500245
RekhaAparna01c532b182025-02-19 19:56:49 -0600246 return true;
247 }
248 catch (const std::exception& l_ex)
249 {
250 logging::logMessage(
251 "DBus write failed, error: " + std::string(l_ex.what()));
252 return false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500253 }
254}
255
256/**
257 * @brief API to publish data on PIM
258 *
259 * The API calls notify on PIM object to publlish VPD.
260 *
261 * @param[in] objectMap - Object, its interface and data.
262 * @return bool - Status of call to PIM notify.
263 */
264inline bool callPIM(types::ObjectMap&& objectMap)
265{
266 try
267 {
268 for (const auto& l_objectKeyValue : objectMap)
269 {
270 auto l_nodeHandle = objectMap.extract(l_objectKeyValue.first);
271
272 if (l_nodeHandle.key().str.find(constants::pimPath, 0) !=
273 std::string::npos)
274 {
275 l_nodeHandle.key() = l_nodeHandle.key().str.replace(
276 0, std::strlen(constants::pimPath), "");
277 objectMap.insert(std::move(l_nodeHandle));
278 }
279 }
280
281 auto bus = sdbusplus::bus::new_default();
282 auto pimMsg =
283 bus.new_method_call(constants::pimServiceName, constants::pimPath,
284 constants::pimIntf, "Notify");
285 pimMsg.append(std::move(objectMap));
286 bus.call(pimMsg);
287 }
288 catch (const sdbusplus::exception::SdBusError& e)
289 {
290 return false;
291 }
292 return true;
293}
294
295/**
296 * @brief API to check if a D-Bus service is running or not.
297 *
298 * Any failure in calling the method "NameHasOwner" implies that the service is
299 * not in a running state. Hence the API returns false in case of any exception
300 * as well.
301 *
302 * @param[in] i_serviceName - D-Bus service name whose status is to be checked.
303 * @return bool - True if the service is running, false otherwise.
304 */
305inline bool isServiceRunning(const std::string& i_serviceName)
306{
307 bool l_retVal = false;
308
309 try
310 {
311 auto l_bus = sdbusplus::bus::new_default();
312 auto l_method = l_bus.new_method_call(
313 "org.freedesktop.DBus", "/org/freedesktop/DBus",
314 "org.freedesktop.DBus", "NameHasOwner");
315 l_method.append(i_serviceName);
316
317 l_bus.call(l_method).read(l_retVal);
318 }
319 catch (const sdbusplus::exception::SdBusError& l_ex)
320 {
321 logging::logMessage(
322 "Call to check service status failed with exception: " +
323 std::string(l_ex.what()));
324 }
325
326 return l_retVal;
327}
328
329/**
330 * @brief API to call "GetAttribute" method uner BIOS manager.
331 *
332 * The API reads the given attribuute from BIOS and returns a tuple of both
333 * current as well as pending value for that attribute.
334 * The API return only the current attribute value if found.
335 * API returns an empty variant of type BiosAttributeCurrentValue in case of any
336 * error.
337 *
338 * @param[in] i_attributeName - Attribute to be read.
339 * @return Tuple of PLDM attribute Type, current attribute value and pending
340 * attribute value.
341 */
Patrick Williams43fedab2025-02-03 14:28:05 -0500342inline types::BiosAttributeCurrentValue biosGetAttributeMethodCall(
343 const std::string& i_attributeName)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500344{
345 auto l_bus = sdbusplus::bus::new_default();
346 auto l_method = l_bus.new_method_call(
347 constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
348 constants::biosConfigMgrInterface, "GetAttribute");
349 l_method.append(i_attributeName);
350
351 types::BiosGetAttrRetType l_attributeVal;
352 try
353 {
354 auto l_result = l_bus.call(l_method);
355 l_result.read(std::get<0>(l_attributeVal), std::get<1>(l_attributeVal),
356 std::get<2>(l_attributeVal));
357 }
358 catch (const sdbusplus::exception::SdBusError& l_ex)
359 {
360 logging::logMessage(
361 "Failed to read BIOS Attribute: " + i_attributeName +
362 " due to error " + std::string(l_ex.what()));
363
364 // TODO: Log an informational PEL here.
365 }
366
367 return std::get<1>(l_attributeVal);
368}
369
370/**
371 * @brief API to check if Chassis is powered on.
372 *
373 * This API queries Phosphor Chassis State Manager to know whether
374 * Chassis is powered on.
375 *
376 * @return true if chassis is powered on, false otherwise
377 */
378inline bool isChassisPowerOn()
379{
380 auto powerState = dbusUtility::readDbusProperty(
381 "xyz.openbmc_project.State.Chassis",
382 "/xyz/openbmc_project/state/chassis0",
383 "xyz.openbmc_project.State.Chassis", "CurrentPowerState");
384
385 if (auto curPowerState = std::get_if<std::string>(&powerState))
386 {
387 if ("xyz.openbmc_project.State.Chassis.PowerState.On" == *curPowerState)
388 {
389 return true;
390 }
391 return false;
392 }
393
394 /*
395 TODO: Add PEL.
396 Callout: Firmware callout
397 Type: Informational
398 Description: Chassis state can't be determined, defaulting to chassis
399 off. : e.what()
400 */
401 return false;
402}
403
404/**
405 * @brief API to check if host is in running state.
406 *
407 * This API reads the current host state from D-bus and returns true if the host
408 * is running.
409 *
410 * @return true if host is in running state. false otherwise.
411 */
412inline bool isHostRunning()
413{
414 const auto l_hostState = dbusUtility::readDbusProperty(
415 constants::hostService, constants::hostObjectPath,
416 constants::hostInterface, "CurrentHostState");
417
418 if (const auto l_currHostState = std::get_if<std::string>(&l_hostState))
419 {
420 if (*l_currHostState == constants::hostRunningState)
421 {
422 return true;
423 }
424 }
425
426 return false;
427}
428
429/**
430 * @brief API to check if BMC is in ready state.
431 *
432 * This API reads the current state of BMC from D-bus and returns true if BMC is
433 * in ready state.
434 *
435 * @return true if BMC is ready, false otherwise.
436 */
437inline bool isBMCReady()
438{
439 const auto l_bmcState = dbusUtility::readDbusProperty(
440 constants::bmcStateService, constants::bmcZeroStateObject,
441 constants::bmcStateInterface, constants::currentBMCStateProperty);
442
443 if (const auto l_currBMCState = std::get_if<std::string>(&l_bmcState))
444 {
445 if (*l_currBMCState == constants::bmcReadyState)
446 {
447 return true;
448 }
449 }
450
451 return false;
452}
453
454/**
455 * @brief An API to enable BMC reboot guard
456 *
457 * This API does a D-Bus method call to enable BMC reboot guard.
458 *
459 * @return On success, returns 0, otherwise returns -1.
460 */
461inline int EnableRebootGuard() noexcept
462{
463 int l_rc{constants::FAILURE};
464 try
465 {
466 auto l_bus = sdbusplus::bus::new_default();
467 auto l_method = l_bus.new_method_call(
468 constants::systemdService, constants::systemdObjectPath,
469 constants::systemdManagerInterface, "StartUnit");
470 l_method.append("reboot-guard-enable.service", "replace");
471 l_bus.call_noreply(l_method);
472 l_rc = constants::SUCCESS;
473 }
474 catch (const sdbusplus::exception::SdBusError& l_ex)
475 {
476 std::string l_errMsg =
477 "D-Bus call to enable BMC reboot guard failed for reason: ";
478 l_errMsg += l_ex.what();
479
480 logging::logMessage(l_errMsg);
481 }
482 return l_rc;
483}
484
485/**
486 * @brief An API to disable BMC reboot guard
487 *
488 * This API disables BMC reboot guard. This API has an inbuilt re-try mechanism.
489 * If Disable Reboot Guard fails, this API attempts to Disable Reboot Guard for
490 * 3 more times at an interval of 333ms.
491 *
492 * @return On success, returns 0, otherwise returns -1.
493 */
494inline int DisableRebootGuard() noexcept
495{
496 int l_rc{constants::FAILURE};
497
498 // A lambda which executes the DBus call to disable BMC reboot guard.
499 auto l_executeDisableRebootGuard = []() -> int {
500 int l_dBusCallRc{constants::FAILURE};
501 try
502 {
503 auto l_bus = sdbusplus::bus::new_default();
504 auto l_method = l_bus.new_method_call(
505 constants::systemdService, constants::systemdObjectPath,
506 constants::systemdManagerInterface, "StartUnit");
507 l_method.append("reboot-guard-disable.service", "replace");
508 l_bus.call_noreply(l_method);
509 l_dBusCallRc = constants::SUCCESS;
510 }
511 catch (const sdbusplus::exception::SdBusError& l_ex)
512 {}
513 return l_dBusCallRc;
514 };
515
516 if (constants::FAILURE == l_executeDisableRebootGuard())
517 {
518 std::function<void()> l_retryDisableRebootGuard;
519
520 // A lambda which tries to disable BMC reboot guard for 3 times at an
521 // interval of 333 ms.
522 l_retryDisableRebootGuard = [&]() {
523 constexpr int MAX_RETRIES{3};
524 static int l_numRetries{0};
525
526 if (l_numRetries < MAX_RETRIES)
527 {
528 l_numRetries++;
529 if (constants::FAILURE == l_executeDisableRebootGuard())
530 {
531 // sleep for 333ms before next retry. This is just a random
532 // value so that 3 re-tries * 333ms takes ~1 second in the
533 // worst case.
534 const std::chrono::milliseconds l_sleepTime{333};
535 std::this_thread::sleep_for(l_sleepTime);
536 l_retryDisableRebootGuard();
537 }
538 else
539 {
540 l_numRetries = 0;
541 l_rc = constants::SUCCESS;
542 }
543 }
544 else
545 {
546 // Failed to Disable Reboot Guard even after 3 retries.
547 logging::logMessage("Failed to Disable Reboot Guard after " +
548 std::to_string(MAX_RETRIES) + " re-tries");
549 l_numRetries = 0;
550 }
551 };
552
553 l_retryDisableRebootGuard();
554 }
555 else
556 {
557 l_rc = constants::SUCCESS;
558 }
559 return l_rc;
560}
561
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600562/**
563 * @brief API to notify FRU VPD Collection status.
564 *
565 * This API uses PIM's Notify method to update the given FRU VPD collection
566 * status on D-bus.
567 *
568 * @param[in] i_inventoryPath - D-bus inventory path
569 * @param[in] i_fruCollectionStatus - FRU VPD collection status.
570 *
571 * @return true if update succeeds, false otherwise.
572 */
573inline bool notifyFRUCollectionStatus(const std::string& i_inventoryPath,
574 const std::string& i_fruCollectionStatus)
575{
576 types::ObjectMap l_objectMap;
577 types::InterfaceMap l_interfaceMap;
578 types::PropertyMap l_propertyMap;
579
580 l_propertyMap.emplace("CollectionStatus", i_fruCollectionStatus);
581 l_interfaceMap.emplace(constants::vpdCollectionInterface, l_propertyMap);
582 l_objectMap.emplace(i_inventoryPath, l_interfaceMap);
583
584 if (!dbusUtility::callPIM(std::move(l_objectMap)))
585 {
586 return false;
587 }
588
589 return true;
590}
Sunny Srivastava1a48f0c2025-02-19 19:02:03 +0530591
592/**
593 * @brief API to read IM keyword from Dbus.
594 *
595 * @return IM value read from Dbus, Empty in case of any error.
596 */
597inline types::BinaryVector getImFromDbus()
598{
599 const auto& l_retValue = dbusUtility::readDbusProperty(
600 constants::pimServiceName, constants::systemVpdInvPath,
601 constants::vsbpInf, constants::kwdIM);
602
603 auto l_imValue = std::get_if<types::BinaryVector>(&l_retValue);
604 if (!l_imValue || (*l_imValue).size() != constants::VALUE_4)
605 {
606 return types::BinaryVector{};
607 }
608
609 return *l_imValue;
610}
Sunny Srivastavac6ef42d2025-02-19 19:17:10 +0530611
612/**
613 * @brief API to return prefix of functional firmware image.
614 *
615 * Every functional image belongs to a series which is denoted by the first two
616 * characters of the image name. The API extracts that and returns it to the
617 * caller.
618 *
619 * @return Two character string, empty string in case of any error.
620 */
621inline std::string getImagePrefix()
622{
623 try
624 {
625 types::DbusVariantType l_retVal = readDbusProperty(
626 constants::objectMapperService, constants::functionalImageObjPath,
627 constants::associationInterface, "endpoints");
628
629 auto l_listOfFunctionalPath =
630 std::get_if<std::vector<std::string>>(&l_retVal);
631
632 if (!l_listOfFunctionalPath || (*l_listOfFunctionalPath).empty())
633 {
634 throw DbusException("failed to get functional image path.");
635 }
636
637 for (const auto& l_imagePath : *l_listOfFunctionalPath)
638 {
639 types::DbusVariantType l_retValPriority =
640 readDbusProperty(constants::imageUpdateService, l_imagePath,
641 constants::imagePrirotyInf, "Priority");
642
643 auto l_imagePriority = std::get_if<uint8_t>(&l_retValPriority);
644 if (!l_imagePriority)
645 {
646 throw DbusException(
647 "failed to read functional image priority for path [" +
648 l_imagePath + "]");
649 }
650
651 // only interested in running image.
652 if (*l_imagePriority != constants::VALUE_0)
653 {
654 continue;
655 }
656
657 types::DbusVariantType l_retExVer = readDbusProperty(
658 constants::imageUpdateService, l_imagePath,
659 constants::imageExtendedVerInf, "ExtendedVersion");
660
661 auto l_imageExtendedVersion = std::get_if<std::string>(&l_retExVer);
662 if (!l_imageExtendedVersion)
663 {
664 throw DbusException(
665 "Unable to read extended version for the functional image [" +
666 l_imagePath + "]");
667 }
668
669 if ((*l_imageExtendedVersion).empty() ||
670 (*l_imageExtendedVersion).length() <= constants::VALUE_2)
671 {
672 throw DbusException("Invalid extended version read for path [" +
673 l_imagePath + "]");
674 }
675
676 // return first two character from image name.
677 return (*l_imageExtendedVersion)
678 .substr(constants::VALUE_0, constants::VALUE_2);
679 }
680 throw std::runtime_error("No Image found with required priority.");
681 }
682 catch (const std::exception& l_ex)
683 {
684 logging::logMessage(l_ex.what());
685 return std::string{};
686 }
687}
Sunny Srivastavaf1dda762025-02-19 23:46:19 +0530688
689/**
690 * @brief API to read DBus present property for the given inventory.
691 *
692 * @param[in] i_invObjPath - Inventory path.
693 * @return Present property value, false in case of any error.
694 */
695inline bool isInventoryPresent(const std::string& i_invObjPath)
696{
697 if (i_invObjPath.empty())
698 {
699 return false;
700 }
701
702 const auto& l_retValue =
703 dbusUtility::readDbusProperty(constants::pimServiceName, i_invObjPath,
704 constants::inventoryItemInf, "Present");
705
706 auto l_ptrPresence = std::get_if<bool>(&l_retValue);
707 if (!l_ptrPresence)
708 {
709 return false;
710 }
711
712 return (*l_ptrPresence);
713}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500714} // namespace dbusUtility
715} // namespace vpd