xref: /openbmc/pldm/common/utils.hpp (revision 70262ed7bf854b25d4b65628bc3c892ddfe9380f)
1 #pragma once
2 
3 #include "instance_id.hpp"
4 #include "types.hpp"
5 
6 #include <libpldm/base.h>
7 #include <libpldm/bios.h>
8 #include <libpldm/entity.h>
9 #include <libpldm/pdr.h>
10 #include <libpldm/platform.h>
11 #include <libpldm/utils.h>
12 #include <systemd/sd-bus.h>
13 #include <unistd.h>
14 
15 #include <nlohmann/json.hpp>
16 #include <sdbusplus/server.hpp>
17 #include <xyz/openbmc_project/Inventory/Manager/client.hpp>
18 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
19 #include <xyz/openbmc_project/ObjectMapper/client.hpp>
20 
21 #include <cstdint>
22 #include <deque>
23 #include <exception>
24 #include <expected>
25 #include <filesystem>
26 #include <iostream>
27 #include <map>
28 #include <string>
29 #include <variant>
30 #include <vector>
31 
32 constexpr uint64_t dbusTimeout =
33     std::chrono::duration_cast<std::chrono::microseconds>(
34         std::chrono::seconds(DBUS_TIMEOUT))
35         .count();
36 
37 namespace pldm
38 {
39 namespace utils
40 {
41 
42 enum class Level
43 {
44     WARNING,
45     CRITICAL,
46     PERFORMANCELOSS,
47     SOFTSHUTDOWN,
48     HARDSHUTDOWN,
49     ERROR
50 };
51 enum class Direction
52 {
53     HIGH,
54     LOW,
55     ERROR
56 };
57 
58 const std::set<std::string_view> dbusValueTypeNames = {
59     "bool",    "uint8_t",  "int16_t",         "uint16_t",
60     "int32_t", "uint32_t", "int64_t",         "uint64_t",
61     "double",  "string",   "vector<uint8_t>", "vector<string>"};
62 const std::set<std::string_view> dbusValueNumericTypeNames = {
63     "uint8_t",  "int16_t", "uint16_t", "int32_t",
64     "uint32_t", "int64_t", "uint64_t", "double"};
65 
66 namespace fs = std::filesystem;
67 using Json = nlohmann::json;
68 constexpr bool Tx = true;
69 constexpr bool Rx = false;
70 using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>;
71 using inventoryManager =
72     sdbusplus::client::xyz::openbmc_project::inventory::Manager<>;
73 
74 constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
75 constexpr auto mapperService = ObjectMapper::default_service;
76 constexpr auto inventoryPath = "/xyz/openbmc_project/inventory";
77 
78 /** @struct CustomFD
79  *
80  *  RAII wrapper for file descriptor.
81  */
82 struct CustomFD
83 {
84     CustomFD(const CustomFD&) = delete;
85     CustomFD& operator=(const CustomFD&) = delete;
86     CustomFD(CustomFD&&) = delete;
87     CustomFD& operator=(CustomFD&&) = delete;
88 
CustomFDpldm::utils::CustomFD89     CustomFD(int fd) : fd(fd) {}
90 
~CustomFDpldm::utils::CustomFD91     ~CustomFD()
92     {
93         if (fd >= 0)
94         {
95             close(fd);
96         }
97     }
98 
operator ()pldm::utils::CustomFD99     int operator()() const
100     {
101         return fd;
102     }
103 
104   private:
105     int fd = -1;
106 };
107 
108 /** @brief Calculate the pad for PLDM data
109  *
110  *  @param[in] data - Length of the data
111  *  @return - uint8_t - number of pad bytes
112  */
113 uint8_t getNumPadBytes(uint32_t data);
114 
115 /** @brief Convert uint64 to date
116  *
117  *  @param[in] data - time date of uint64
118  *  @param[out] year - year number in dec
119  *  @param[out] month - month number in dec
120  *  @param[out] day - day of the month in dec
121  *  @param[out] hour - number of hours in dec
122  *  @param[out] min - number of minutes in dec
123  *  @param[out] sec - number of seconds in dec
124  *  @return true if decode success, false if decode failed
125  */
126 bool uintToDate(uint64_t data, uint16_t* year, uint8_t* month, uint8_t* day,
127                 uint8_t* hour, uint8_t* min, uint8_t* sec);
128 
129 /** @brief Convert effecter data to structure of set_effecter_state_field
130  *
131  *  @param[in] effecterData - the date of effecter
132  *  @param[in] effecterCount - the number of individual sets of effecter
133  *                              information
134  *  @return[out] parse success and get a valid set_effecter_state_field
135  *               structure, return nullopt means parse failed
136  */
137 std::optional<std::vector<set_effecter_state_field>> parseEffecterData(
138     const std::vector<uint8_t>& effecterData, uint8_t effecterCount);
139 
140 /**
141  *  @brief creates an error log
142  *  @param[in] errorMsg - the error message
143  */
144 void reportError(const char* errorMsg);
145 
146 /** @brief Convert any Decimal number to BCD
147  *
148  *  @tparam[in] decimal - Decimal number
149  *  @return Corresponding BCD number
150  */
151 template <typename T>
decimalToBcd(T decimal)152 T decimalToBcd(T decimal)
153 {
154     T bcd = 0;
155     T rem = 0;
156     auto cnt = 0;
157 
158     while (decimal)
159     {
160         rem = decimal % 10;
161         bcd = bcd + (rem << cnt);
162         decimal = decimal / 10;
163         cnt += 4;
164     }
165 
166     return bcd;
167 }
168 
169 /**
170  * @brief Unwraps the result of InstanceId allocation and logs errors.
171  *
172  * Logs errors if present, but always returns the original result so the caller
173  * can choose to handle the error (return, throw, etc).
174  *
175  * @tparam T      Instance ID value type.
176  * @param[in] result  The result from InstanceIdDb::next().
177  * @return std::expected<T, InstanceIdError>
178  *     Returns the original result (value or error).
179  */
180 template <typename T>
getInstanceId(const std::expected<T,pldm::InstanceIdError> & result)181 std::expected<T, pldm::InstanceIdError> getInstanceId(
182     const std::expected<T, pldm::InstanceIdError>& result)
183 {
184     if (!result)
185     {
186         std::cerr << result.error().msg() << std::endl;
187     }
188     return result;
189 }
190 
191 struct DBusMapping
192 {
193     std::string objectPath;   //!< D-Bus object path
194     std::string interface;    //!< D-Bus interface
195     std::string propertyName; //!< D-Bus property name
196     std::string propertyType; //!< D-Bus property type
197 };
198 
199 using PropertyValue =
200     std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t,
201                  uint64_t, double, std::string, std::vector<uint8_t>,
202                  std::vector<uint64_t>, std::vector<std::string>>;
203 using DbusProp = std::string;
204 using DbusChangedProps = std::map<DbusProp, PropertyValue>;
205 using DBusInterfaceAdded = std::vector<
206     std::pair<pldm::dbus::Interface,
207               std::vector<std::pair<pldm::dbus::Property,
208                                     std::variant<pldm::dbus::Property>>>>>;
209 
210 using ObjectPath = std::string;
211 using EntityName = std::string;
212 using Entities = std::vector<pldm_entity_node*>;
213 using EntityAssociations = std::vector<Entities>;
214 using ObjectPathMaps = std::map<fs::path, pldm_entity_node*>;
215 using EntityMaps = std::map<pldm::pdr::EntityType, EntityName>;
216 
217 using ServiceName = std::string;
218 using Interfaces = std::vector<std::string>;
219 using MapperServiceMap = std::vector<std::pair<ServiceName, Interfaces>>;
220 using GetSubTreeResponse = std::vector<std::pair<ObjectPath, MapperServiceMap>>;
221 using GetSubTreePathsResponse = std::vector<std::string>;
222 using GetAssociatedSubTreeResponse =
223     std::map<std::string, std::map<std::string, std::vector<std::string>>>;
224 using GetAncestorsResponse =
225     std::vector<std::pair<ObjectPath, MapperServiceMap>>;
226 using PropertyMap = std::map<std::string, PropertyValue>;
227 using InterfaceMap = std::map<std::string, PropertyMap>;
228 using ObjectValueTree = std::map<sdbusplus::message::object_path, InterfaceMap>;
229 
230 /**
231  * @brief The interface for DBusHandler
232  */
233 class DBusHandlerInterface
234 {
235   public:
236     virtual ~DBusHandlerInterface() = default;
237 
238     virtual std::string getService(const char* path,
239                                    const char* interface) const = 0;
240     virtual GetSubTreeResponse getSubtree(
241         const std::string& path, int depth,
242         const std::vector<std::string>& ifaceList) const = 0;
243 
244     virtual GetSubTreePathsResponse getSubTreePaths(
245         const std::string& objectPath, int depth,
246         const std::vector<std::string>& ifaceList) const = 0;
247 
248     virtual GetAncestorsResponse getAncestors(
249         const std::string& path,
250         const std::vector<std::string>& ifaceList) const = 0;
251 
252     virtual void setDbusProperty(const DBusMapping& dBusMap,
253                                  const PropertyValue& value) const = 0;
254 
255     virtual PropertyValue getDbusPropertyVariant(
256         const char* objPath, const char* dbusProp,
257         const char* dbusInterface) const = 0;
258 
259     virtual PropertyMap getDbusPropertiesVariant(
260         const char* serviceName, const char* objPath,
261         const char* dbusInterface) const = 0;
262 
263     virtual GetAssociatedSubTreeResponse getAssociatedSubTree(
264         const sdbusplus::message::object_path& objectPath,
265         const sdbusplus::message::object_path& subtree, int depth,
266         const std::vector<std::string>& ifaceList) const = 0;
267 };
268 
269 /**
270  *  @class DBusHandler
271  *
272  *  Wrapper class to handle the D-Bus calls
273  *
274  *  This class contains the APIs to handle the D-Bus calls
275  *  to cater the request from pldm requester.
276  *  A class is created to mock the apis in the test cases
277  */
278 class DBusHandler : public DBusHandlerInterface
279 {
280   public:
281     /** @brief Get the bus connection. */
getBus()282     static auto& getBus()
283     {
284         static auto bus = sdbusplus::bus::new_default();
285         return bus;
286     }
287 
288     /**
289      *  @brief Get the DBUS Service name for the input dbus path
290      *
291      *  @param[in] path - DBUS object path
292      *  @param[in] interface - DBUS Interface
293      *
294      *  @return std::string - the dbus service name
295      *
296      *  @throw sdbusplus::exception_t when it fails
297      */
298     std::string getService(const char* path,
299                            const char* interface) const override;
300 
301     /**
302      *  @brief Get the Subtree response from the mapper
303      *
304      *  @param[in] path - DBUS object path
305      *  @param[in] depth - Search depth
306      *  @param[in] ifaceList - list of the interface that are being
307      *                         queried from the mapper
308      *
309      *  @return GetSubTreeResponse - the mapper subtree response
310      *
311      *  @throw sdbusplus::exception_t when it fails
312      */
313     GetSubTreeResponse getSubtree(
314         const std::string& path, int depth,
315         const std::vector<std::string>& ifaceList) const override;
316 
317     /** @brief Get Subtree path response from the mapper
318      *
319      *  @param[in] path - DBUS object path
320      *  @param[in] depth - Search depth
321      *  @param[in] ifaceList - list of the interface that are being
322      *                         queried from the mapper
323      *
324      *  @return std::vector<std::string> vector of subtree paths
325      */
326     GetSubTreePathsResponse getSubTreePaths(
327         const std::string& objectPath, int depth,
328         const std::vector<std::string>& ifaceList) const override;
329 
330     /**
331      *  @brief Get the Ancestors response from the mapper
332      *
333      *  @param[in] path - D-Bus object path
334      *  @param[in] ifaceList - an optional list of interfaces to constrain the
335      *                         search to queried from the mapper
336      *
337      *  @return GetAncestorsResponse - the mapper GetAncestors response
338      *
339      *  @throw sdbusplus::exception_t when it fails
340      */
341     GetAncestorsResponse getAncestors(
342         const std::string& path,
343         const std::vector<std::string>& ifaceList) const override;
344 
345     /** @brief Get property(type: variant) from the requested dbus
346      *
347      *  @param[in] objPath - The Dbus object path
348      *  @param[in] dbusProp - The property name to get
349      *  @param[in] dbusInterface - The Dbus interface
350      *
351      *  @return The value of the property(type: variant)
352      *
353      *  @throw sdbusplus::exception_t when it fails
354      */
355     PropertyValue getDbusPropertyVariant(
356         const char* objPath, const char* dbusProp,
357         const char* dbusInterface) const override;
358 
359     /** @brief Get All properties(type: variant) from the requested dbus
360      *
361      *  @param[in] serviceName - The Dbus service name
362      *  @param[in] objPath - The Dbus object path
363      *  @param[in] dbusInterface - The Dbus interface
364      *
365      *  @return The values of the properties(type: variant)
366      *
367      *  @throw sdbusplus::exception_t when it fails
368      */
369     PropertyMap getDbusPropertiesVariant(
370         const char* serviceName, const char* objPath,
371         const char* dbusInterface) const override;
372 
373     /** @brief The template function to get property from the requested dbus
374      *         path
375      *
376      *  @tparam Property - Excepted type of the property on dbus
377      *
378      *  @param[in] objPath - The Dbus object path
379      *  @param[in] dbusProp - The property name to get
380      *  @param[in] dbusInterface - The Dbus interface
381      *
382      *  @return The value of the property
383      *
384      *  @throw sdbusplus::exception_t when dbus request fails
385      *         std::bad_variant_access when \p Property and property on dbus do
386      *         not match
387      */
388     template <typename Property>
getDbusProperty(const char * objPath,const char * dbusProp,const char * dbusInterface)389     auto getDbusProperty(const char* objPath, const char* dbusProp,
390                          const char* dbusInterface)
391     {
392         auto VariantValue =
393             getDbusPropertyVariant(objPath, dbusProp, dbusInterface);
394         return std::get<Property>(VariantValue);
395     }
396 
397     /** @brief Get the associated subtree from the mapper
398      *
399      * @param[in] path - The D-Bus object path
400      *
401      * @param[in] interface - The D-Bus interface
402      *
403      * @return GetAssociatedSubtreeResponse - The associated subtree
404      */
405     GetAssociatedSubTreeResponse getAssociatedSubTree(
406         const sdbusplus::message::object_path& objectPath,
407         const sdbusplus::message::object_path& subtree, int depth,
408         const std::vector<std::string>& ifaceList) const override;
409 
410     /** @brief Set Dbus property
411      *
412      *  @param[in] dBusMap - Object path, property name, interface and property
413      *                       type for the D-Bus object
414      *  @param[in] value - The value to be set
415      *
416      *  @throw sdbusplus::exception_t when it fails
417      */
418     void setDbusProperty(const DBusMapping& dBusMap,
419                          const PropertyValue& value) const override;
420 
421     /** @brief This function retrieves the properties of an object managed
422      *         by the specified D-Bus service located at the given object path.
423      *
424      *  @param[in] service - The D-Bus service providing the managed object
425      *  @param[in] value - The object path of the managed object
426      *
427      *  @return A hierarchical structure representing the properties of the
428      *          managed object.
429      *  @throw sdbusplus::exception_t when it fails
430      */
431     static ObjectValueTree getManagedObj(const char* service, const char* path);
432 
433     /** @brief Retrieve the inventory objects managed by a specified class.
434      *         The retrieved inventory objects are cached statically
435      *         and returned upon subsequent calls to this function.
436      *
437      *  @tparam ClassType - The class type that manages the inventory objects.
438      *
439      *  @return A reference to the cached inventory objects.
440      */
441     template <typename ClassType>
getInventoryObjects()442     static auto& getInventoryObjects()
443     {
444         static ObjectValueTree object = ClassType::getManagedObj(
445             inventoryManager::interface, inventoryPath);
446         return object;
447     }
448 };
449 
450 /** @brief Fetch parent D-Bus object based on pathname
451  *
452  *  @param[in] dbusObj - child D-Bus object
453  *
454  *  @return std::string - the parent D-Bus object path
455  */
findParent(const std::string & dbusObj)456 inline std::string findParent(const std::string& dbusObj)
457 {
458     fs::path p(dbusObj);
459     return p.parent_path().string();
460 }
461 
462 /** @brief Read (static) MCTP EID of host firmware from a file
463  *
464  *  @return uint8_t - MCTP EID
465  */
466 uint8_t readHostEID();
467 
468 /** @brief Validate the MCTP EID of MCTP endpoint
469  *         In `Table 2 - Special endpoint IDs` of DSP0236. EID 0 is NULL_EID.
470  *         EID from 1 to 7 is reserved EID. EID 0xFF is broadcast EID.
471  *         Those are invalid EID of one MCTP Endpoint.
472  *
473  * @param[in] eid - MCTP EID
474  *
475  * @return true if the MCTP EID is valid otherwise return false.
476  */
477 bool isValidEID(eid mctpEid);
478 
479 /** @brief Convert a value in the JSON to a D-Bus property value
480  *
481  *  @param[in] type - type of the D-Bus property
482  *  @param[in] value - value in the JSON file
483  *
484  *  @return PropertyValue - the D-Bus property value
485  */
486 PropertyValue jsonEntryToDbusVal(std::string_view type,
487                                  const nlohmann::json& value);
488 
489 /** @brief Find State Effecter PDR
490  *  @param[in] tid - PLDM terminus ID.
491  *  @param[in] entityID - entity that can be associated with PLDM State set.
492  *  @param[in] stateSetId - value that identifies PLDM State set.
493  *  @param[in] repo - pointer to BMC's primary PDR repo.
494  *  @return array[array[uint8_t]] - StateEffecterPDRs
495  */
496 std::vector<std::vector<uint8_t>> findStateEffecterPDR(
497     uint8_t tid, uint16_t entityID, uint16_t stateSetId, const pldm_pdr* repo);
498 /** @brief Find State Sensor PDR
499  *  @param[in] tid - PLDM terminus ID.
500  *  @param[in] entityID - entity that can be associated with PLDM State set.
501  *  @param[in] stateSetId - value that identifies PLDM State set.
502  *  @param[in] repo - pointer to BMC's primary PDR repo.
503  *  @return array[array[uint8_t]] - StateSensorPDRs
504  */
505 std::vector<std::vector<uint8_t>> findStateSensorPDR(
506     uint8_t tid, uint16_t entityID, uint16_t stateSetId, const pldm_pdr* repo);
507 
508 /** @brief Find sensor id from a state sensor PDR
509  *
510  *  @param[in] pdrRepo - PDR repository
511  *  @param[in] tid - terminus id
512  *  @param[in] entityType - entity type
513  *  @param[in] entityInstance - entity instance number
514  *  @param[in] containerId - container id
515  *  @param[in] stateSetId - state set id
516  *
517  *  @return uint16_t - the sensor id
518  */
519 uint16_t findStateSensorId(const pldm_pdr* pdrRepo, uint8_t tid,
520                            uint16_t entityType, uint16_t entityInstance,
521                            uint16_t containerId, uint16_t stateSetId);
522 
523 /** @brief Find effecter id from a state effecter pdr
524  *  @param[in] pdrRepo - PDR repository
525  *  @param[in] entityType - entity type
526  *  @param[in] entityInstance - entity instance number
527  *  @param[in] containerId - container id
528  *  @param[in] stateSetId - state set id
529  *  @param[in] localOrRemote - true for checking local repo and false for remote
530  *                             repo
531  *
532  *  @return uint16_t - the effecter id
533  */
534 uint16_t findStateEffecterId(const pldm_pdr* pdrRepo, uint16_t entityType,
535                              uint16_t entityInstance, uint16_t containerId,
536                              uint16_t stateSetId, bool localOrRemote);
537 
538 /** @brief Emit the sensor event signal
539  *
540  *	@param[in] tid - the terminus id
541  *  @param[in] sensorId - sensorID value of the sensor
542  *  @param[in] sensorOffset - Identifies which state sensor within a
543  * composite state sensor the event is being returned for
544  *  @param[in] eventState - The event state value from the state change that
545  * triggered the event message
546  *  @param[in] previousEventState - The event state value for the state from
547  * which the present event state was entered.
548  *  @return PLDM completion code
549  */
550 int emitStateSensorEventSignal(uint8_t tid, uint16_t sensorId,
551                                uint8_t sensorOffset, uint8_t eventState,
552                                uint8_t previousEventState);
553 
554 /**
555  *  @brief call Recover() method to recover an MCTP Endpoint
556  *  @param[in] MCTP Endpoint's object path
557  */
558 void recoverMctpEndpoint(const std::string& endpointObjPath);
559 
560 /** @brief Print the buffer
561  *
562  *  @param[in]  isTx - True if the buffer is an outgoing PLDM message, false if
563                        the buffer is an incoming PLDM message
564  *  @param[in]  buffer - Buffer to print
565  *
566  *  @return - None
567  */
568 void printBuffer(bool isTx, const std::vector<uint8_t>& buffer);
569 
570 /** @brief Convert the buffer to std::string
571  *
572  *  If there are characters that are not printable characters, it is replaced
573  *  with space(0x20).
574  *
575  *  @param[in] var - pointer to data and length of the data
576  *
577  *  @return std::string equivalent of variable field
578  */
579 std::string toString(const struct variable_field& var);
580 
581 /** @brief Split strings according to special identifiers
582  *
583  *  We can split the string according to the custom identifier(';', ',', '&' or
584  *  others) and store it to vector.
585  *
586  *  @param[in] srcStr       - The string to be split
587  *  @param[in] delim        - The custom identifier
588  *  @param[in] trimStr      - The first and last string to be trimmed
589  *
590  *  @return std::vector<std::string> Vectors are used to store strings
591  */
592 std::vector<std::string> split(std::string_view srcStr, std::string_view delim,
593                                std::string_view trimStr = "");
594 /** @brief Get the current system time in readable format
595  *
596  *  @return - std::string equivalent of the system time
597  */
598 std::string getCurrentSystemTime();
599 
600 /** @brief checks if the FRU is actually present.
601  *  @param[in] objPath - FRU object path.
602  *
603  *  @return bool to indicate presence or absence of FRU.
604  */
605 bool checkForFruPresence(const std::string& objPath);
606 
607 /** @brief Method to check if the logical bit is set
608  *
609  *  @param[containerId] - container id of the entity
610  *
611  *  @return true or false based on the logic bit set
612  */
613 bool checkIfLogicalBitSet(const uint16_t& containerId);
614 
615 /** @brief setting the present property
616  *
617  *  @param[in] objPath - the object path of the fru
618  *  @param[in] present - status to set either true/false
619  */
620 void setFruPresence(const std::string& fruObjPath, bool present);
621 
622 /** @brief Trim `\0` in string and replace ` ` by `_` to use name in D-Bus
623  *         object path
624  *
625  *  @param[in] name - the input string
626  *
627  *  @return the result string
628  */
629 std::string_view trimNameForDbus(std::string& name);
630 
631 /** @brief Convert the number type D-Bus Value to the double
632  *
633  *  @param[in] type - string type should in dbusValueNumericTypeNames list
634  *  @param[in] value - DBus PropertyValue variant
635  *  @param[out] doubleValue - response value
636  *
637  *  @return true if data type is corrected and converting is successful
638  *          otherwise return false.
639  */
640 bool dbusPropValuesToDouble(const std::string_view& type,
641                             const pldm::utils::PropertyValue& value,
642                             double* doubleValue);
643 
644 /** @brief Convert the Fru String bytes from PLDM Fru to std::string
645  *
646  *  @param[in] value - the Fru String bytes
647  *  @param[in] length - Number of bytes
648  *
649  *  @return Fru string or nullopt.
650  */
651 std::optional<std::string> fruFieldValuestring(const uint8_t* value,
652                                                const uint8_t& length);
653 
654 /** @brief Convert the Fru Uint32 raw data from PLDM Fru to uint32_t
655  *
656  *  @param[in] value - the Fru uint32 raw data
657  *  @param[in] length - Number of bytes
658  *
659  *  @return Fru uint32_t or nullopt.
660  */
661 std::optional<uint32_t> fruFieldParserU32(const uint8_t* value,
662                                           const uint8_t& length);
663 
664 } // namespace utils
665 } // namespace pldm
666