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