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