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