xref: /openbmc/pldm/common/utils.hpp (revision 549e4bc8bf5504b407ffc1976ab557dcad6c6028)
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 
CustomFDpldm::utils::CustomFD86     CustomFD(int fd) : fd(fd) {}
87 
~CustomFDpldm::utils::CustomFD88     ~CustomFD()
89     {
90         if (fd >= 0)
91         {
92             close(fd);
93         }
94     }
95 
operator ()pldm::utils::CustomFD96     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>
decimalToBcd(T decimal)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 GetAncestorsResponse =
198     std::vector<std::pair<ObjectPath, MapperServiceMap>>;
199 using PropertyMap = std::map<std::string, PropertyValue>;
200 using InterfaceMap = std::map<std::string, PropertyMap>;
201 using ObjectValueTree = std::map<sdbusplus::message::object_path, InterfaceMap>;
202 
203 /**
204  * @brief The interface for DBusHandler
205  */
206 class DBusHandlerInterface
207 {
208   public:
209     virtual ~DBusHandlerInterface() = default;
210 
211     virtual std::string getService(const char* path,
212                                    const char* interface) const = 0;
213     virtual GetSubTreeResponse getSubtree(
214         const std::string& path, int depth,
215         const std::vector<std::string>& ifaceList) const = 0;
216 
217     virtual GetSubTreePathsResponse getSubTreePaths(
218         const std::string& objectPath, int depth,
219         const std::vector<std::string>& ifaceList) const = 0;
220 
221     virtual GetAncestorsResponse getAncestors(
222         const std::string& path,
223         const std::vector<std::string>& ifaceList) const = 0;
224 
225     virtual void setDbusProperty(const DBusMapping& dBusMap,
226                                  const PropertyValue& value) const = 0;
227 
228     virtual PropertyValue getDbusPropertyVariant(
229         const char* objPath, const char* dbusProp,
230         const char* dbusInterface) const = 0;
231 
232     virtual PropertyMap getDbusPropertiesVariant(
233         const char* serviceName, const char* objPath,
234         const char* dbusInterface) const = 0;
235 };
236 
237 /**
238  *  @class DBusHandler
239  *
240  *  Wrapper class to handle the D-Bus calls
241  *
242  *  This class contains the APIs to handle the D-Bus calls
243  *  to cater the request from pldm requester.
244  *  A class is created to mock the apis in the test cases
245  */
246 class DBusHandler : public DBusHandlerInterface
247 {
248   public:
249     /** @brief Get the bus connection. */
getBus()250     static auto& getBus()
251     {
252         static auto bus = sdbusplus::bus::new_default();
253         return bus;
254     }
255 
256     /**
257      *  @brief Get the DBUS Service name for the input dbus path
258      *
259      *  @param[in] path - DBUS object path
260      *  @param[in] interface - DBUS Interface
261      *
262      *  @return std::string - the dbus service name
263      *
264      *  @throw sdbusplus::exception_t when it fails
265      */
266     std::string getService(const char* path,
267                            const char* interface) const override;
268 
269     /**
270      *  @brief Get the Subtree response from the mapper
271      *
272      *  @param[in] path - DBUS object path
273      *  @param[in] depth - Search depth
274      *  @param[in] ifaceList - list of the interface that are being
275      *                         queried from the mapper
276      *
277      *  @return GetSubTreeResponse - the mapper subtree response
278      *
279      *  @throw sdbusplus::exception_t when it fails
280      */
281     GetSubTreeResponse getSubtree(
282         const std::string& path, int depth,
283         const std::vector<std::string>& ifaceList) const override;
284 
285     /** @brief Get Subtree path response from the mapper
286      *
287      *  @param[in] path - DBUS object path
288      *  @param[in] depth - Search depth
289      *  @param[in] ifaceList - list of the interface that are being
290      *                         queried from the mapper
291      *
292      *  @return std::vector<std::string> vector of subtree paths
293      */
294     GetSubTreePathsResponse getSubTreePaths(
295         const std::string& objectPath, int depth,
296         const std::vector<std::string>& ifaceList) const override;
297 
298     /**
299      *  @brief Get the Ancestors response from the mapper
300      *
301      *  @param[in] path - D-Bus object path
302      *  @param[in] ifaceList - an optional list of interfaces to constrain the
303      *                         search to queried from the mapper
304      *
305      *  @return GetAncestorsResponse - the mapper GetAncestors response
306      *
307      *  @throw sdbusplus::exception_t when it fails
308      */
309     GetAncestorsResponse getAncestors(
310         const std::string& path,
311         const std::vector<std::string>& ifaceList) const override;
312 
313     /** @brief Get property(type: variant) from the requested dbus
314      *
315      *  @param[in] objPath - The Dbus object path
316      *  @param[in] dbusProp - The property name to get
317      *  @param[in] dbusInterface - The Dbus interface
318      *
319      *  @return The value of the property(type: variant)
320      *
321      *  @throw sdbusplus::exception_t when it fails
322      */
323     PropertyValue getDbusPropertyVariant(
324         const char* objPath, const char* dbusProp,
325         const char* dbusInterface) const override;
326 
327     /** @brief Get All properties(type: variant) from the requested dbus
328      *
329      *  @param[in] serviceName - The Dbus service name
330      *  @param[in] objPath - The Dbus object path
331      *  @param[in] dbusInterface - The Dbus interface
332      *
333      *  @return The values of the properties(type: variant)
334      *
335      *  @throw sdbusplus::exception_t when it fails
336      */
337     PropertyMap getDbusPropertiesVariant(
338         const char* serviceName, const char* objPath,
339         const char* dbusInterface) const override;
340 
341     /** @brief The template function to get property from the requested dbus
342      *         path
343      *
344      *  @tparam Property - Excepted type of the property on dbus
345      *
346      *  @param[in] objPath - The Dbus object path
347      *  @param[in] dbusProp - The property name to get
348      *  @param[in] dbusInterface - The Dbus interface
349      *
350      *  @return The value of the property
351      *
352      *  @throw sdbusplus::exception_t when dbus request fails
353      *         std::bad_variant_access when \p Property and property on dbus do
354      *         not match
355      */
356     template <typename Property>
getDbusProperty(const char * objPath,const char * dbusProp,const char * dbusInterface)357     auto getDbusProperty(const char* objPath, const char* dbusProp,
358                          const char* dbusInterface)
359     {
360         auto VariantValue =
361             getDbusPropertyVariant(objPath, dbusProp, dbusInterface);
362         return std::get<Property>(VariantValue);
363     }
364 
365     /** @brief Set Dbus property
366      *
367      *  @param[in] dBusMap - Object path, property name, interface and property
368      *                       type for the D-Bus object
369      *  @param[in] value - The value to be set
370      *
371      *  @throw sdbusplus::exception_t when it fails
372      */
373     void setDbusProperty(const DBusMapping& dBusMap,
374                          const PropertyValue& value) const override;
375 
376     /** @brief This function retrieves the properties of an object managed
377      *         by the specified D-Bus service located at the given object path.
378      *
379      *  @param[in] service - The D-Bus service providing the managed object
380      *  @param[in] value - The object path of the managed object
381      *
382      *  @return A hierarchical structure representing the properties of the
383      *          managed object.
384      *  @throw sdbusplus::exception_t when it fails
385      */
386     static ObjectValueTree getManagedObj(const char* service, const char* path);
387 
388     /** @brief Retrieve the inventory objects managed by a specified class.
389      *         The retrieved inventory objects are cached statically
390      *         and returned upon subsequent calls to this function.
391      *
392      *  @tparam ClassType - The class type that manages the inventory objects.
393      *
394      *  @return A reference to the cached inventory objects.
395      */
396     template <typename ClassType>
getInventoryObjects()397     static auto& getInventoryObjects()
398     {
399         static ObjectValueTree object = ClassType::getManagedObj(
400             inventoryManager::interface, inventoryPath);
401         return object;
402     }
403 };
404 
405 /** @brief Fetch parent D-Bus object based on pathname
406  *
407  *  @param[in] dbusObj - child D-Bus object
408  *
409  *  @return std::string - the parent D-Bus object path
410  */
findParent(const std::string & dbusObj)411 inline std::string findParent(const std::string& dbusObj)
412 {
413     fs::path p(dbusObj);
414     return p.parent_path().string();
415 }
416 
417 /** @brief Read (static) MCTP EID of host firmware from a file
418  *
419  *  @return uint8_t - MCTP EID
420  */
421 uint8_t readHostEID();
422 
423 /** @brief Validate the MCTP EID of MCTP endpoint
424  *         In `Table 2 - Special endpoint IDs` of DSP0236. EID 0 is NULL_EID.
425  *         EID from 1 to 7 is reserved EID. EID 0xFF is broadcast EID.
426  *         Those are invalid EID of one MCTP Endpoint.
427  *
428  * @param[in] eid - MCTP EID
429  *
430  * @return true if the MCTP EID is valid otherwise return false.
431  */
432 bool isValidEID(eid mctpEid);
433 
434 /** @brief Convert a value in the JSON to a D-Bus property value
435  *
436  *  @param[in] type - type of the D-Bus property
437  *  @param[in] value - value in the JSON file
438  *
439  *  @return PropertyValue - the D-Bus property value
440  */
441 PropertyValue jsonEntryToDbusVal(std::string_view type,
442                                  const nlohmann::json& value);
443 
444 /** @brief Find State Effecter PDR
445  *  @param[in] tid - PLDM terminus ID.
446  *  @param[in] entityID - entity that can be associated with PLDM State set.
447  *  @param[in] stateSetId - value that identifies PLDM State set.
448  *  @param[in] repo - pointer to BMC's primary PDR repo.
449  *  @return array[array[uint8_t]] - StateEffecterPDRs
450  */
451 std::vector<std::vector<uint8_t>> findStateEffecterPDR(
452     uint8_t tid, uint16_t entityID, uint16_t stateSetId, const pldm_pdr* repo);
453 /** @brief Find State Sensor PDR
454  *  @param[in] tid - PLDM terminus ID.
455  *  @param[in] entityID - entity that can be associated with PLDM State set.
456  *  @param[in] stateSetId - value that identifies PLDM State set.
457  *  @param[in] repo - pointer to BMC's primary PDR repo.
458  *  @return array[array[uint8_t]] - StateSensorPDRs
459  */
460 std::vector<std::vector<uint8_t>> findStateSensorPDR(
461     uint8_t tid, uint16_t entityID, uint16_t stateSetId, const pldm_pdr* repo);
462 
463 /** @brief Find sensor id from a state sensor PDR
464  *
465  *  @param[in] pdrRepo - PDR repository
466  *  @param[in] tid - terminus id
467  *  @param[in] entityType - entity type
468  *  @param[in] entityInstance - entity instance number
469  *  @param[in] containerId - container id
470  *  @param[in] stateSetId - state set id
471  *
472  *  @return uint16_t - the sensor id
473  */
474 uint16_t findStateSensorId(const pldm_pdr* pdrRepo, uint8_t tid,
475                            uint16_t entityType, uint16_t entityInstance,
476                            uint16_t containerId, uint16_t stateSetId);
477 
478 /** @brief Find effecter id from a state effecter pdr
479  *  @param[in] pdrRepo - PDR repository
480  *  @param[in] entityType - entity type
481  *  @param[in] entityInstance - entity instance number
482  *  @param[in] containerId - container id
483  *  @param[in] stateSetId - state set id
484  *  @param[in] localOrRemote - true for checking local repo and false for remote
485  *                             repo
486  *
487  *  @return uint16_t - the effecter id
488  */
489 uint16_t findStateEffecterId(const pldm_pdr* pdrRepo, uint16_t entityType,
490                              uint16_t entityInstance, uint16_t containerId,
491                              uint16_t stateSetId, bool localOrRemote);
492 
493 /** @brief Emit the sensor event signal
494  *
495  *	@param[in] tid - the terminus id
496  *  @param[in] sensorId - sensorID value of the sensor
497  *  @param[in] sensorOffset - Identifies which state sensor within a
498  * composite state sensor the event is being returned for
499  *  @param[in] eventState - The event state value from the state change that
500  * triggered the event message
501  *  @param[in] previousEventState - The event state value for the state from
502  * which the present event state was entered.
503  *  @return PLDM completion code
504  */
505 int emitStateSensorEventSignal(uint8_t tid, uint16_t sensorId,
506                                uint8_t sensorOffset, uint8_t eventState,
507                                uint8_t previousEventState);
508 
509 /** @brief Print the buffer
510  *
511  *  @param[in]  isTx - True if the buffer is an outgoing PLDM message, false if
512                        the buffer is an incoming PLDM message
513  *  @param[in]  buffer - Buffer to print
514  *
515  *  @return - None
516  */
517 void printBuffer(bool isTx, const std::vector<uint8_t>& buffer);
518 
519 /** @brief Convert the buffer to std::string
520  *
521  *  If there are characters that are not printable characters, it is replaced
522  *  with space(0x20).
523  *
524  *  @param[in] var - pointer to data and length of the data
525  *
526  *  @return std::string equivalent of variable field
527  */
528 std::string toString(const struct variable_field& var);
529 
530 /** @brief Split strings according to special identifiers
531  *
532  *  We can split the string according to the custom identifier(';', ',', '&' or
533  *  others) and store it to vector.
534  *
535  *  @param[in] srcStr       - The string to be split
536  *  @param[in] delim        - The custom identifier
537  *  @param[in] trimStr      - The first and last string to be trimmed
538  *
539  *  @return std::vector<std::string> Vectors are used to store strings
540  */
541 std::vector<std::string> split(std::string_view srcStr, std::string_view delim,
542                                std::string_view trimStr = "");
543 /** @brief Get the current system time in readable format
544  *
545  *  @return - std::string equivalent of the system time
546  */
547 std::string getCurrentSystemTime();
548 
549 /** @brief checks if the FRU is actually present.
550  *  @param[in] objPath - FRU object path.
551  *
552  *  @return bool to indicate presence or absence of FRU.
553  */
554 bool checkForFruPresence(const std::string& objPath);
555 
556 /** @brief Method to check if the logical bit is set
557  *
558  *  @param[containerId] - container id of the entity
559  *
560  *  @return true or false based on the logic bit set
561  */
562 bool checkIfLogicalBitSet(const uint16_t& containerId);
563 
564 /** @brief setting the present property
565  *
566  *  @param[in] objPath - the object path of the fru
567  *  @param[in] present - status to set either true/false
568  */
569 void setFruPresence(const std::string& fruObjPath, bool present);
570 
571 /** @brief Trim `\0` in string and replace ` ` by `_` to use name in D-Bus
572  *         object path
573  *
574  *  @param[in] name - the input string
575  *
576  *  @return the result string
577  */
578 std::string_view trimNameForDbus(std::string& name);
579 
580 /** @brief Convert the number type D-Bus Value to the double
581  *
582  *  @param[in] type - string type should in dbusValueNumericTypeNames list
583  *  @param[in] value - DBus PropertyValue variant
584  *  @param[out] doubleValue - response value
585  *
586  *  @return true if data type is corrected and converting is successful
587  *          otherwise return false.
588  */
589 bool dbusPropValuesToDouble(const std::string_view& type,
590                             const pldm::utils::PropertyValue& value,
591                             double* doubleValue);
592 
593 /** @brief Convert the Fru String bytes from PLDM Fru to std::string
594  *
595  *  @param[in] value - the Fru String bytes
596  *  @param[in] length - Number of bytes
597  *
598  *  @return Fru string or nullopt.
599  */
600 std::optional<std::string> fruFieldValuestring(const uint8_t* value,
601                                                const uint8_t& length);
602 
603 /** @brief Convert the Fru Uint32 raw data from PLDM Fru to uint32_t
604  *
605  *  @param[in] value - the Fru uint32 raw data
606  *  @param[in] length - Number of bytes
607  *
608  *  @return Fru uint32_t or nullopt.
609  */
610 std::optional<uint32_t> fruFieldParserU32(const uint8_t* value,
611                                           const uint8_t& length);
612 
613 } // namespace utils
614 } // namespace pldm
615