xref: /openbmc/pldm/common/utils.hpp (revision 4d99c311)
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 <stdint.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 
20 #include <deque>
21 #include <exception>
22 #include <filesystem>
23 #include <iostream>
24 #include <map>
25 #include <string>
26 #include <variant>
27 #include <vector>
28 
29 constexpr uint64_t dbusTimeout =
30     std::chrono::duration_cast<std::chrono::microseconds>(
31         std::chrono::seconds(DBUS_TIMEOUT))
32         .count();
33 
34 namespace pldm
35 {
36 using Severity = pldm::PelSeverity;
37 
38 // mapping of severity enum to severity interface
39 static std::unordered_map<Severity, std::string> sevMap = {
40     {Severity::Informational,
41      "xyz.openbmc_project.Logging.Entry.Level.Informational"},
42     {Severity::Debug, "xyz.openbmc_project.Logging.Entry.Level.Debug"},
43     {Severity::Notice, "xyz.openbmc_project.Logging.Entry.Level.Notice"},
44     {Severity::Warning, "xyz.openbmc_project.Logging.Entry.Level.Warning"},
45     {Severity::Critical, "xyz.openbmc_project.Logging.Entry.Level.Critical"},
46     {Severity::Emergency, "xyz.openbmc_project.Logging.Entry.Level.Emergency"},
47     {Severity::Error, "xyz.openbmc_project.Logging.Entry.Level.Error"},
48     {Severity::Alert, "xyz.openbmc_project.Logging.Entry.Level.Alert"}};
49 
50 namespace utils
51 {
52 namespace fs = std::filesystem;
53 using Json = nlohmann::json;
54 constexpr bool Tx = true;
55 constexpr bool Rx = false;
56 
57 using EntityName = std::string;
58 using EntityType = uint16_t;
59 
60 using Entities = std::vector<pldm_entity_node*>;
61 using EntityAssociations = std::vector<Entities>;
62 using ObjectPathMaps = std::map<fs::path, pldm_entity_node*>;
63 
64 const std::map<EntityType, EntityName> entityMaps = {
65     {PLDM_ENTITY_SYSTEM_CHASSIS, "chassis"},
66     {PLDM_ENTITY_BOARD, "io_board"},
67     {PLDM_ENTITY_SYS_BOARD, "motherboard"},
68     {PLDM_ENTITY_POWER_SUPPLY, "powersupply"},
69     {PLDM_ENTITY_PROC, "cpu"},
70     {PLDM_ENTITY_SYSTEM_CHASSIS | 0x8000, "system"},
71     {PLDM_ENTITY_PROC_MODULE, "dcm"},
72     {PLDM_ENTITY_PROC | 0x8000, "core"},
73     {PLDM_ENTITY_IO_MODULE, "io_module"},
74     {PLDM_ENTITY_FAN, "fan"},
75     {PLDM_ENTITY_SYS_MGMT_MODULE, "system_management_module"},
76     {PLDM_ENTITY_POWER_CONVERTER, "power_converter"},
77     {PLDM_ENTITY_SLOT, "slot"},
78     {PLDM_ENTITY_CONNECTOR, "connector"}};
79 
80 /** @brief Vector a entity name to pldm_entity from entity association tree
81  *  @param[in]  entityAssoc    - Vector of associated pldm entities
82  *  @param[in]  entityTree     - entity association tree
83  *  @param[out] objPathMap     - maps an object path to pldm_entity from the
84  *                               BMC's entity association tree
85  *  @return
86  */
87 void updateEntityAssociation(const EntityAssociations& entityAssoc,
88                              pldm_entity_association_tree* entityTree,
89                              ObjectPathMaps& objPathMap);
90 
91 /** @struct CustomFD
92  *
93  *  RAII wrapper for file descriptor.
94  */
95 struct CustomFD
96 {
97     CustomFD(const CustomFD&) = delete;
98     CustomFD& operator=(const CustomFD&) = delete;
99     CustomFD(CustomFD&&) = delete;
100     CustomFD& operator=(CustomFD&&) = delete;
101 
102     CustomFD(int fd) : fd(fd) {}
103 
104     ~CustomFD()
105     {
106         if (fd >= 0)
107         {
108             close(fd);
109         }
110     }
111 
112     int operator()() const
113     {
114         return fd;
115     }
116 
117   private:
118     int fd = -1;
119 };
120 
121 /** @brief Calculate the pad for PLDM data
122  *
123  *  @param[in] data - Length of the data
124  *  @return - uint8_t - number of pad bytes
125  */
126 uint8_t getNumPadBytes(uint32_t data);
127 
128 /** @brief Convert uint64 to date
129  *
130  *  @param[in] data - time date of uint64
131  *  @param[out] year - year number in dec
132  *  @param[out] month - month number in dec
133  *  @param[out] day - day of the month in dec
134  *  @param[out] hour - number of hours in dec
135  *  @param[out] min - number of minutes in dec
136  *  @param[out] sec - number of seconds in dec
137  *  @return true if decode success, false if decode faild
138  */
139 bool uintToDate(uint64_t data, uint16_t* year, uint8_t* month, uint8_t* day,
140                 uint8_t* hour, uint8_t* min, uint8_t* sec);
141 
142 /** @brief Convert effecter data to structure of set_effecter_state_field
143  *
144  *  @param[in] effecterData - the date of effecter
145  *  @param[in] effecterCount - the number of individual sets of effecter
146  *                              information
147  *  @return[out] parse success and get a valid set_effecter_state_field
148  *               structure, return nullopt means parse failed
149  */
150 std::optional<std::vector<set_effecter_state_field>>
151     parseEffecterData(const std::vector<uint8_t>& effecterData,
152                       uint8_t effecterCount);
153 
154 /**
155  *  @brief creates an error log
156  *  @param[in] errorMsg - the error message
157  *  @param[in] sev - severity of the log
158  *
159  */
160 void reportError(const char* errorMsg,
161                  const PelSeverity& sev = pldm::PelSeverity::Error);
162 
163 /** @brief Convert any Decimal number to BCD
164  *
165  *  @tparam[in] decimal - Decimal number
166  *  @return Corresponding BCD number
167  */
168 template <typename T>
169 T decimalToBcd(T decimal)
170 {
171     T bcd = 0;
172     T rem = 0;
173     auto cnt = 0;
174 
175     while (decimal)
176     {
177         rem = decimal % 10;
178         bcd = bcd + (rem << cnt);
179         decimal = decimal / 10;
180         cnt += 4;
181     }
182 
183     return bcd;
184 }
185 
186 using inventoryManager =
187     sdbusplus::client::xyz::openbmc_project::inventory::Manager<>;
188 
189 constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
190 constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
191 constexpr auto inventoryPath = "/xyz/openbmc_project/inventory";
192 
193 struct DBusMapping
194 {
195     std::string objectPath;   //!< D-Bus object path
196     std::string interface;    //!< D-Bus interface
197     std::string propertyName; //!< D-Bus property name
198     std::string propertyType; //!< D-Bus property type
199 };
200 
201 using PropertyValue =
202     std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t,
203                  uint64_t, double, std::string, std::vector<uint8_t>,
204                  std::vector<std::string>>;
205 using DbusProp = std::string;
206 using DbusChangedProps = std::map<DbusProp, PropertyValue>;
207 using DBusInterfaceAdded = std::vector<
208     std::pair<pldm::dbus::Interface,
209               std::vector<std::pair<pldm::dbus::Property,
210                                     std::variant<pldm::dbus::Property>>>>>;
211 using ObjectPath = std::string;
212 using ServiceName = std::string;
213 using Interfaces = std::vector<std::string>;
214 using MapperServiceMap = std::vector<std::pair<ServiceName, Interfaces>>;
215 using GetSubTreeResponse = std::vector<std::pair<ObjectPath, MapperServiceMap>>;
216 using PropertyMap = std::map<std::string, PropertyValue>;
217 using InterfaceMap = std::map<std::string, PropertyMap>;
218 using ObjectValueTree = std::map<sdbusplus::message::object_path, InterfaceMap>;
219 
220 /**
221  * @brief The interface for DBusHandler
222  */
223 class DBusHandlerInterface
224 {
225   public:
226     virtual ~DBusHandlerInterface() = default;
227 
228     virtual std::string getService(const char* path,
229                                    const char* interface) const = 0;
230     virtual GetSubTreeResponse
231         getSubtree(const std::string& path, int depth,
232                    const std::vector<std::string>& ifaceList) const = 0;
233 
234     virtual void setDbusProperty(const DBusMapping& dBusMap,
235                                  const PropertyValue& value) const = 0;
236 
237     virtual PropertyValue
238         getDbusPropertyVariant(const char* objPath, const char* dbusProp,
239                                const char* dbusInterface) const = 0;
240 };
241 
242 /**
243  *  @class DBusHandler
244  *
245  *  Wrapper class to handle the D-Bus calls
246  *
247  *  This class contains the APIs to handle the D-Bus calls
248  *  to cater the request from pldm requester.
249  *  A class is created to mock the apis in the test cases
250  */
251 class DBusHandler : public DBusHandlerInterface
252 {
253   public:
254     /** @brief Get the bus connection. */
255     static auto& getBus()
256     {
257         static auto bus = sdbusplus::bus::new_default();
258         return bus;
259     }
260 
261     /**
262      *  @brief Get the DBUS Service name for the input dbus path
263      *
264      *  @param[in] path - DBUS object path
265      *  @param[in] interface - DBUS Interface
266      *
267      *  @return std::string - the dbus service name
268      *
269      *  @throw sdbusplus::exception_t when it fails
270      */
271     std::string getService(const char* path,
272                            const char* interface) const override;
273 
274     /**
275      *  @brief Get the Subtree response from the mapper
276      *
277      *  @param[in] path - DBUS object path
278      *  @param[in] depth - Search depth
279      *  @param[in] ifaceList - list of the interface that are being
280      *                         queried from the mapper
281      *
282      *  @return GetSubTreeResponse - the mapper subtree response
283      *
284      *  @throw sdbusplus::exception_t when it fails
285      */
286     GetSubTreeResponse
287         getSubtree(const std::string& path, int depth,
288                    const std::vector<std::string>& ifaceList) const override;
289 
290     /** @brief Get property(type: variant) from the requested dbus
291      *
292      *  @param[in] objPath - The Dbus object path
293      *  @param[in] dbusProp - The property name to get
294      *  @param[in] dbusInterface - The Dbus interface
295      *
296      *  @return The value of the property(type: variant)
297      *
298      *  @throw sdbusplus::exception_t when it fails
299      */
300     PropertyValue
301         getDbusPropertyVariant(const char* objPath, const char* dbusProp,
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>
320     auto getDbusProperty(const char* objPath, const char* dbusProp,
321                          const char* dbusInterface)
322     {
323         auto VariantValue = getDbusPropertyVariant(objPath, dbusProp,
324                                                    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>
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  */
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 Convert a value in the JSON to a D-Bus property value
387  *
388  *  @param[in] type - type of the D-Bus property
389  *  @param[in] value - value in the JSON file
390  *
391  *  @return PropertyValue - the D-Bus property value
392  */
393 PropertyValue jsonEntryToDbusVal(std::string_view type,
394                                  const nlohmann::json& value);
395 
396 /** @brief Find State Effecter PDR
397  *  @param[in] tid - PLDM terminus ID.
398  *  @param[in] entityID - entity that can be associated with PLDM State set.
399  *  @param[in] stateSetId - value that identifies PLDM State set.
400  *  @param[in] repo - pointer to BMC's primary PDR repo.
401  *  @return array[array[uint8_t]] - StateEffecterPDRs
402  */
403 std::vector<std::vector<uint8_t>> findStateEffecterPDR(uint8_t tid,
404                                                        uint16_t entityID,
405                                                        uint16_t stateSetId,
406                                                        const pldm_pdr* repo);
407 /** @brief Find State Sensor 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]] - StateSensorPDRs
413  */
414 std::vector<std::vector<uint8_t>> findStateSensorPDR(uint8_t tid,
415                                                      uint16_t entityID,
416                                                      uint16_t stateSetId,
417                                                      const pldm_pdr* repo);
418 
419 /** @brief Find sensor id from a state sensor PDR
420  *
421  *  @param[in] pdrRepo - PDR repository
422  *  @param[in] tid - terminus id
423  *  @param[in] entityType - entity type
424  *  @param[in] entityInstance - entity instance number
425  *  @param[in] containerId - container id
426  *  @param[in] stateSetId - state set id
427  *
428  *  @return uint16_t - the sensor id
429  */
430 uint16_t findStateSensorId(const pldm_pdr* pdrRepo, uint8_t tid,
431                            uint16_t entityType, uint16_t entityInstance,
432                            uint16_t containerId, uint16_t stateSetId);
433 
434 /** @brief Find effecter id from a state effecter pdr
435  *  @param[in] pdrRepo - PDR repository
436  *  @param[in] entityType - entity type
437  *  @param[in] entityInstance - entity instance number
438  *  @param[in] containerId - container id
439  *  @param[in] stateSetId - state set id
440  *  @param[in] localOrRemote - true for checking local repo and false for remote
441  *                             repo
442  *
443  *  @return uint16_t - the effecter id
444  */
445 uint16_t findStateEffecterId(const pldm_pdr* pdrRepo, uint16_t entityType,
446                              uint16_t entityInstance, uint16_t containerId,
447                              uint16_t stateSetId, bool localOrRemote);
448 
449 /** @brief Emit the sensor event signal
450  *
451  *	@param[in] tid - the terminus id
452  *  @param[in] sensorId - sensorID value of the sensor
453  *  @param[in] sensorOffset - Identifies which state sensor within a
454  * composite state sensor the event is being returned for
455  *  @param[in] eventState - The event state value from the state change that
456  * triggered the event message
457  *  @param[in] previousEventState - The event state value for the state from
458  * which the present event state was entered.
459  *  @return PLDM completion code
460  */
461 int emitStateSensorEventSignal(uint8_t tid, uint16_t sensorId,
462                                uint8_t sensorOffset, uint8_t eventState,
463                                uint8_t previousEventState);
464 
465 /** @brief Print the buffer
466  *
467  *  @param[in]  isTx - True if the buffer is an outgoing PLDM message, false if
468                        the buffer is an incoming PLDM message
469  *  @param[in]  buffer - Buffer to print
470  *
471  *  @return - None
472  */
473 void printBuffer(bool isTx, const std::vector<uint8_t>& buffer);
474 
475 /** @brief Convert the buffer to std::string
476  *
477  *  If there are characters that are not printable characters, it is replaced
478  *  with space(0x20).
479  *
480  *  @param[in] var - pointer to data and length of the data
481  *
482  *  @return std::string equivalent of variable field
483  */
484 std::string toString(const struct variable_field& var);
485 
486 /** @brief Split strings according to special identifiers
487  *
488  *  We can split the string according to the custom identifier(';', ',', '&' or
489  *  others) and store it to vector.
490  *
491  *  @param[in] srcStr       - The string to be split
492  *  @param[in] delim        - The custom identifier
493  *  @param[in] trimStr      - The first and last string to be trimmed
494  *
495  *  @return std::vector<std::string> Vectors are used to store strings
496  */
497 std::vector<std::string> split(std::string_view srcStr, std::string_view delim,
498                                std::string_view trimStr = "");
499 /** @brief Get the current system time in readable format
500  *
501  *  @return - std::string equivalent of the system time
502  */
503 std::string getCurrentSystemTime();
504 
505 /** @brief checks if the FRU is actually present.
506  *  @param[in] objPath - FRU object path.
507  *
508  *  @return bool to indicate presence or absence of FRU.
509  */
510 bool checkForFruPresence(const std::string& objPath);
511 
512 /** @brief Method to check if the logical bit is set
513  *
514  *  @param[containerId] - container id of the entity
515  *
516  *  @return true or false based on the logic bit set
517  */
518 bool checkIfLogicalBitSet(const uint16_t& containerId);
519 
520 /** @brief setting the present property
521  *
522  *  @param[in] objPath - the object path of the fru
523  *  @param[in] present - status to set either true/false
524  */
525 void setFruPresence(const std::string& fruObjPath, bool present);
526 } // namespace utils
527 } // namespace pldm
528