| /* |
| * SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION & |
| * AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include "base.h" |
| |
| #include "coroutine.hpp" |
| #include "log.hpp" |
| #include "types.hpp" |
| |
| #include <cxxabi.h> // abi::__cxa_demangle |
| #include <stdint.h> |
| #include <sys/mman.h> // for memfd_create |
| #include <sys/stat.h> // for fstat |
| #include <systemd/sd-bus.h> |
| #include <unistd.h> |
| #include <unistd.h> // for write and lseek |
| |
| #include <boost/asio.hpp> |
| #include <nlohmann/json.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/server.hpp> |
| #include <xyz/openbmc_project/Software/SecurityCommon/common.hpp> |
| |
| #include <exception> |
| #include <filesystem> |
| #include <iostream> |
| #include <queue> |
| #include <string> |
| #include <typeinfo> // typeid().name() |
| #include <variant> |
| #include <vector> |
| |
| struct DBusMapping |
| { |
| std::string objectPath; //!< D-Bus object path |
| std::string interface; //!< D-Bus interface |
| std::string propertyName; //!< D-Bus property name |
| std::string propertyType; //!< D-Bus property type |
| }; |
| |
| using PropertyValue = std::variant< |
| bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, |
| double, std::string, std::vector<sdbusplus::message::object_path>, |
| std::vector<std::string>, std::vector<uint64_t>, |
| std::vector<std::tuple<std::string, std::string, std::string>>>; |
| using DbusProp = std::string; |
| using DbusChangedProps = std::map<DbusProp, PropertyValue>; |
| using ObjectPath = std::string; |
| using ServiceName = std::string; |
| using MapperServiceMap = std::vector<std::pair<ServiceName, dbus::Interfaces>>; |
| using GetSubTreeResponse = std::vector<std::pair<ObjectPath, MapperServiceMap>>; |
| using GetAssociatedObjectsResponse = std::variant<std::vector<ObjectPath>>; |
| using PropertyValuesCollection = |
| std::vector<std::pair<DbusProp, PropertyValue>>; |
| |
| #define UUID_INT_SIZE 16 |
| #define UUID_LEN 36 |
| #define MAC_ADDRESS_DATA_LEN 6 |
| |
| // Largest safe integer for double precision (2^53 - 1) |
| const uint64_t MAX_SAFE_INTEGER_IN_DOUBLE = (1ULL << 53) - 1; |
| |
| namespace utils |
| { |
| constexpr bool Tx = true; |
| constexpr bool Rx = false; |
| |
| constexpr auto dbusProperties = "org.freedesktop.DBus.Properties"; |
| constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper"; |
| constexpr auto mapperPath = "/xyz/openbmc_project/object_mapper"; |
| constexpr auto mapperInterface = "xyz.openbmc_project.ObjectMapper"; |
| |
| struct Association |
| { |
| std::string forward; |
| std::string backward; |
| std::string absolutePath; |
| }; |
| using Associations = |
| std::vector<std::tuple<std::string, std::string, std::string>>; |
| |
| struct Bitfield256 : bitfield256_t |
| { |
| /** |
| * @brief Constructor for Bitfield256, clears all the bits |
| * |
| */ |
| Bitfield256(); |
| /** |
| * @brief Set the bit at the given bitNumber |
| * |
| * @param bitNumber Number of the bit to set |
| * @return true if the bit was not set before |
| */ |
| bool setBit(const uint8_t& bitNumber); |
| /** |
| * @brief Check if any bit is set |
| * |
| * @return true if any bit is set |
| */ |
| bool isAnyBitSet() const; |
| /** |
| * @brief Get comma separated list of set bits |
| * |
| * @return Comma separated list of set bits |
| */ |
| std::string getSetBits() const; |
| /** |
| * @brief Clear all the bits |
| * |
| */ |
| void clear(); |
| }; |
| |
| /** @struct CustomFD |
| * |
| * RAII wrapper for file descriptor. |
| */ |
| struct CustomFD |
| { |
| CustomFD(const CustomFD&) = delete; |
| CustomFD& operator=(const CustomFD&) = delete; |
| CustomFD(CustomFD&&) = delete; |
| CustomFD& operator=(CustomFD&&) = delete; |
| |
| CustomFD(int fd) : fd(fd) {} |
| |
| ~CustomFD() |
| { |
| if (fd >= 0) |
| { |
| close(fd); |
| } |
| } |
| |
| int operator()() const |
| { |
| return fd; |
| } |
| |
| operator int() const |
| { |
| return fd; |
| } |
| |
| private: |
| int fd = -1; |
| }; |
| |
| /** |
| * @brief The interface for DBusHandler |
| */ |
| class IDBusHandler |
| { |
| public: |
| virtual ~IDBusHandler() = default; |
| |
| virtual std::string getService(const char* path, |
| const char* interface) const = 0; |
| |
| virtual MapperServiceMap getServiceMap( |
| const char* path, |
| const dbus::Interfaces& ifaceList = dbus::Interfaces()) const = 0; |
| |
| virtual GetSubTreeResponse |
| getSubtree(const std::string& path, int depth, |
| const dbus::Interfaces& ifaceList) const = 0; |
| |
| virtual void setDbusProperty(const DBusMapping& dBusMap, |
| const PropertyValue& value) const = 0; |
| |
| virtual PropertyValue |
| getDbusPropertyVariant(const char* objPath, const char* dbusProp, |
| const char* dbusInterface) const = 0; |
| |
| virtual PropertyValuesCollection |
| getDbusProperties(const char* objPath, |
| const char* dbusInterface) const = 0; |
| |
| virtual GetAssociatedObjectsResponse |
| getAssociatedObjects(const std::string& path, |
| const std::string& association) const = 0; |
| |
| /** @brief The template function to get property from the requested dbus |
| * path |
| * |
| * @tparam Property - Excepted type of the property on dbus |
| * |
| * @param[in] objPath - The Dbus object path |
| * @param[in] dbusProp - The property name to get |
| * @param[in] dbusInterface - The Dbus interface |
| * |
| * @return The value of the property |
| * |
| * @throw sdbusplus::exception::exception when dbus request fails |
| * std::bad_variant_access when \p Property and property on dbus do |
| * not match |
| */ |
| template <typename Property> |
| auto getDbusProperty(const char* objPath, const char* dbusProp, |
| const char* dbusInterface) |
| { |
| auto VariantValue = getDbusPropertyVariant(objPath, dbusProp, |
| dbusInterface); |
| return std::get<Property>(VariantValue); |
| } |
| /** @brief The template function to get optional property from the requested |
| * dbus path without throwing sdbusplus::exception::exception on fail |
| * |
| * @tparam Property - Excepted type of the property on dbus |
| * |
| * @param[in] objPath - The Dbus object path |
| * @param[in] dbusProp - The property name to get |
| * @param[in] dbusInterface - The Dbus interface |
| * |
| * @return The value of the property |
| * |
| * @throw std::bad_variant_access when \p Property and property on dbus do |
| * not match |
| */ |
| template <typename Property> |
| auto tryGetDbusProperty(const char* objPath, const char* dbusProp, |
| const char* dbusInterface, |
| const Property& defValue = Property()) |
| { |
| try |
| { |
| return getDbusProperty<Property>(objPath, dbusProp, dbusInterface); |
| } |
| catch (const sdbusplus::exception::exception&) |
| { |
| return defValue; |
| } |
| } |
| }; |
| |
| /** |
| * @class DBusHandler |
| * |
| * Wrapper class to handle the D-Bus calls |
| * |
| * This class contains the APIs to handle the D-Bus calls |
| * to cater the request from nsm requester. |
| * A class is created to mock the apis in the test cases |
| */ |
| class DBusHandler : public IDBusHandler |
| { |
| public: |
| /** @brief Get the bus connection. */ |
| static auto& getBus() |
| { |
| static auto bus = sdbusplus::bus::new_default(); |
| return bus; |
| } |
| |
| /** @brief Get the asio connection. */ |
| static auto& getAsioConnection() |
| { |
| static boost::asio::io_context io; |
| static auto conn = std::make_shared<sdbusplus::asio::connection>(io); |
| return conn; |
| } |
| |
| /** @brief Get the DBusHandler instance. */ |
| static DBusHandler& instance() |
| { |
| static DBusHandler dBusHandler; |
| return dBusHandler; |
| } |
| |
| /** |
| * @brief Get the DBUS Service name for the input dbus path |
| * |
| * @param[in] path - DBUS object path |
| * @param[in] interface - DBUS Interface |
| * |
| * @return std::string - the dbus service name |
| * |
| * @throw sdbusplus::exception::exception when it fails |
| */ |
| std::string getService(const char* path, |
| const char* interface) const override; |
| |
| /** |
| * @brief Get the DBUS ServiceMap for the input dbus path |
| * |
| * @param[in] path - DBUS object path |
| * @param[in] ifaceList - list of the interface that are being |
| * queried from the mapper |
| * |
| * @return MapperServiceMap - the dbus services map |
| * |
| * @throw sdbusplus::exception::exception when it fails |
| */ |
| MapperServiceMap |
| getServiceMap(const char* path, |
| const dbus::Interfaces& ifaceList) const override; |
| |
| /** |
| * @brief Get the Subtree response from the mapper |
| * |
| * @param[in] path - DBUS object path |
| * @param[in] depth - Search depth |
| * @param[in] ifaceList - list of the interface that are being |
| * queried from the mapper |
| * |
| * @return GetSubTreeResponse - the mapper subtree response |
| * |
| * @throw sdbusplus::exception::exception when it fails |
| */ |
| GetSubTreeResponse |
| getSubtree(const std::string& path, int depth, |
| const dbus::Interfaces& ifaceList) const override; |
| |
| /** @brief Get property(type: variant) from the requested dbus |
| * |
| * @param[in] objPath - The Dbus object path |
| * @param[in] dbusProp - The property name to get |
| * @param[in] dbusInterface - The Dbus interface |
| * |
| * @return The value of the property(type: variant) |
| * |
| * @throw sdbusplus::exception::exception when it fails |
| */ |
| PropertyValue |
| getDbusPropertyVariant(const char* objPath, const char* dbusProp, |
| const char* dbusInterface) const override; |
| |
| /** @brief Set Dbus property |
| * |
| * @param[in] dBusMap - Object path, property name, interface and property |
| * type for the D-Bus object |
| * @param[in] value - The value to be set |
| * |
| * @throw sdbusplus::exception::exception when it fails |
| */ |
| void setDbusProperty(const DBusMapping& dBusMap, |
| const PropertyValue& value) const override; |
| |
| /** @brief Get properties(type: variant) from the requested dbus |
| * |
| * @param[in] objPath - The Dbus object path |
| * @param[in] dbusInterface - The Dbus interface |
| * |
| * @return The collection of the properties (type: variant) |
| * |
| * @throw sdbusplus::exception::exception when it fails |
| */ |
| PropertyValuesCollection |
| getDbusProperties(const char* objPath, |
| const char* dbusInterface) const override; |
| |
| /** |
| * @brief Get the associated object response from the mapper |
| * |
| * @param[in] path - DBUS object path |
| * @param[in] association - forward / reverse association |
| * |
| * @return GetAssociatedObjectsResponse - the mapper get associated object |
| * response |
| * |
| * @throw sdbusplus::exception::exception when it fails |
| */ |
| GetAssociatedObjectsResponse |
| getAssociatedObjects(const std::string& path, |
| const std::string& association) const override; |
| }; |
| |
| /** @brief check if the string is met UTF-8 or not based on the dbus spec. |
| * |
| * @param[in] input - The input string to be checked |
| * @return - boolean |
| */ |
| bool isValidDbusString(std::string_view input); |
| |
| IDBusHandler& DBusHandler(); |
| |
| /** @brief Print the buffer |
| * |
| * @param[in] isTx - True if the buffer is an outgoing NSM message, false if |
| the buffer is an incoming NSM message |
| * @param[in] buffer - Buffer to print |
| * @param[in] tag - Tag to identify the message |
| * @param[in] eid - EID of the message |
| * |
| * @return - None |
| */ |
| void printBuffer(bool isTx, const std::vector<uint8_t>& buffer, uint8_t tag, |
| eid_t eid); |
| |
| /** @brief Print the buffer |
| * |
| * @param[in] isTx - True if the buffer is an outgoing NSM message, false if |
| the buffer is an incoming NSM message |
| * @param[in] buffer - NSM message buffer to log |
| * @param[in] bufferLen - NSM message buffer length |
| * @param[in] tag - Tag to identify the message |
| * @param[in] eid - EID of the message |
| * |
| * @return - None |
| */ |
| void printBuffer(bool isTx, const uint8_t* buffer, size_t bufferLen, |
| uint8_t tag, eid_t eid); |
| |
| /** @brief Split strings according to special identifiers |
| * |
| * We can split the string according to the custom identifier(';', ',', '&' or |
| * others) and store it to vector. |
| * |
| * @param[in] srcStr - The string to be split |
| * @param[in] delim - The custom identifier |
| * @param[in] trimStr - The first and last string to be trimmed |
| * |
| * @return std::vector<std::string> Vectors are used to store strings |
| */ |
| std::vector<std::string> split(std::string_view srcStr, std::string_view delim, |
| std::string_view trimStr = ""); |
| /** @brief Get the current system time in readable format |
| * |
| * @return - std::string equivalent of the system time |
| */ |
| std::string getCurrentSystemTime(); |
| |
| /** @brief Get UUID from the eid |
| * |
| * @return - uuid_t uuid for corresponding eid |
| */ |
| std::optional<std::string> getUUIDFromEID( |
| const std::multimap<std::string, |
| std::tuple<eid_t, MctpMedium, MctpBinding>>& eidTable, |
| eid_t eid); |
| |
| /** @brief Get eid from the UUID |
| * |
| * @return - eid_t eid for corresponding UUID |
| */ |
| eid_t getEidFromUUID( |
| const std::multimap<uuid_t, std::tuple<eid_t, MctpMedium, MctpBinding>>& |
| eidTable, |
| uuid_t uuid); |
| |
| /** @brief UUID conversion from integer array to std::string. |
| * |
| * @param[in] uuidIntArr - The integer array to be converted to string |
| * @return - uuid_t |
| */ |
| uuid_t convertUUIDToString(const std::vector<uint8_t>& uuidIntArr); |
| |
| /** @brief string conversion from integer array to std::string. |
| * |
| * @param[in] data - The integer array to be converted to string |
| * @param[in] dataSize - The size of the string |
| * @return - std::string |
| */ |
| std::string convertHexToString(const std::vector<uint8_t>& data, |
| const size_t dataSize); |
| |
| /** @brief Make valid D-Bus Object Path of Interface Name by replacing unwanted |
| * characters with underscore ('_') |
| * |
| * @param[in] name - potentially invalid D-Bus Name |
| * @return - valid D-Bus Name |
| */ |
| std::string makeDBusNameValid(const std::string& name); |
| |
| /** @brief Get associations of a configuration PDI |
| * |
| * @param[in] objPath - D-Bus Object Path of configuration PDI |
| * @param[in] interfaceSubStr - Sub string to identify association interfaces |
| * at objPath |
| * @return - Associations |
| */ |
| std::vector<Association> getAssociations(const std::string& objPath, |
| const std::string& interfaceSubStr); |
| |
| /** |
| * @brief Converts vector of Association struct to dbus tuples collection |
| * |
| * @param associations vector of Association struct |
| * @return Associations Dbus tuples collection |
| */ |
| Associations getAssociations(const std::vector<Association>& associations); |
| |
| /** @brief Parse bitfield response for nsm command |
| * |
| * @param[in] data - std::vector<uint8_t> |
| * @param[in] const bitfield8_t* value - pointer to bitfield data |
| * @param[in] size - uint_8 varuable to hold bitfield data |
| */ |
| void convertBitMaskToVector(std::vector<uint8_t>& data, |
| const bitfield8_t* value, uint8_t size); |
| |
| /** |
| * @brief Get Device Name associated to Device Type |
| * |
| * @param deviceType NSM device type number |
| */ |
| std::string getDeviceNameFromDeviceType(const uint8_t deviceType); |
| |
| /** |
| * @brief Get Device Instance Name = deviceName_deviceInstanceNumber |
| * |
| * @param deviceType NSM device type number |
| * @param instanceNumber NSM device instance number |
| */ |
| std::string getDeviceInstanceName(const uint8_t deviceType, |
| const uint8_t instanceNumber); |
| |
| /** @brief Get associations of a configuration PDI by coroutine |
| * |
| * @param[in] objPath - D-Bus Object Path of configuration PDI |
| * @param[in] interfaceSubStr - Sub string to identify association interfaces |
| * at objPath |
| * @param[out] - associations |
| */ |
| |
| requester::Coroutine coGetAssociations(const std::string& objPath, |
| const std::string& interfaceSubStr, |
| std::vector<Association>& associations); |
| |
| // Function to convert bitfield256_t to a bitmap |
| /** |
| * Converts a bitfield256_t structure to a bitmap. |
| * |
| * @param bf Pointer to the bitfield256_t structure. |
| **/ |
| std::vector<uint8_t> bitfield256_tToBitMap(bitfield256_t bf); |
| |
| /** |
| * @brief Converts a bitmap into two vectors containing the indices of bits set |
| * to 0 and 1. |
| * |
| * @param[in] bitmap - A vector of bytes representing the bitmap. |
| * @return A pair of vectors, where the first vector contains the indices of |
| * bits that are 0, and the second vector contains the indices of bits that |
| * are 1. |
| * |
| * @note The indices are 0-based, meaning the first bit in the bitmap is index |
| * 0. |
| */ |
| std::pair<std::vector<uint8_t>, std::vector<uint8_t>> |
| bitmapToIndices(const std::vector<uint8_t>& bitmap); |
| |
| /** |
| * @brief Converts a list of indices into a bitmap. |
| * |
| * @param[in] indices - A vector of 1-based indices where bits should be set |
| * to 1. |
| * @param[in] size - (optional) size of the output bitmap. |
| * @return A vector of bytes representing the bitmap. |
| * |
| * @note Indices that are not provided in the list will be set to 0 in the |
| * bitmap. |
| */ |
| std::vector<uint8_t> indicesToBitmap(const std::vector<uint8_t>& indices, |
| const size_t size = 0); |
| |
| std::vector<uint8_t> bitfield256_tToBitArray(bitfield256_t bf); |
| |
| /** |
| * @brief Converts a bitfield representing update methods into a list of |
| * update method enums. |
| * |
| * @param[in] updateMethodBitfield - A bitfield where each bit represents a |
| * different update method. |
| * @return A vector of SecurityCommon::UpdateMethods enums corresponding to |
| * the set bits in the bitfield. |
| * |
| * @note The function checks specific bits in the bitfield and adds the |
| * corresponding update method enum to the returned vector. Only the bits |
| * that are set in the bitfield will have their corresponding enums included |
| * in the list. |
| */ |
| std::vector<sdbusplus::common::xyz::openbmc_project::software::SecurityCommon:: |
| UpdateMethods> |
| updateMethodsBitfieldToList(bitfield32_t updateMethodBitfield); |
| |
| // Function to convert bitmap to a bitfield256_t |
| /** |
| * Converts a bitmap structure to a bitfield256_t. |
| * |
| * @param bitmap Pointer to the bitmap structure. |
| **/ |
| bitfield256_t bitMapToBitfield256_t(const std::vector<uint8_t>& bitmap); |
| |
| std::string vectorTo256BitHexString(const std::vector<uint8_t>& value); |
| |
| /** |
| * @brief Reads the contents of a file descriptor into a buffer. |
| * |
| * @param fd File descriptor to read from. |
| * @param buffer Buffer to store the read data. |
| */ |
| void readFdToBuffer(int fd, std::vector<uint8_t>& buffer); |
| |
| /** |
| * @brief Writes the contents of a buffer to a file descriptor. |
| * |
| * @param fd File descriptor to write to. |
| * @param buffer Buffer to write. |
| */ |
| void writeBufferToFd(int fd, const std::vector<uint8_t>& buffer); |
| /** |
| * @brief Writes the contents of a buffer to a file descriptor. |
| * |
| * @param fd File descriptor to write to. |
| * @param buffer Buffer to write. |
| */ |
| void appendBufferToFd(int fd, const std::vector<uint8_t>& buffer); |
| |
| std::string requestMsgToHexString(std::vector<uint8_t>& requestMsg); |
| |
| /** |
| * @brief Writes the contents of a buffer to a file descriptor. |
| * |
| * @param uint32_t value to be scaled down |
| * @param scaleFactor scaleDown factor. |
| * Eg : value = 200000, will be returned as 200 if scaleFactor = 1000 |
| */ |
| double convertAndScaleDownUint32ToDouble(uint32_t value, double scaleFactor); |
| |
| /** |
| * @brief Convert uint64 to double with safe check. |
| * |
| * @param uint64_t value to be converted to double |
| */ |
| double uint64ToDoubleSafeConvert(uint64_t value); |
| |
| /** |
| * @brief Convert int64 to double with safe check. |
| * |
| * @param uint64_t value to be converted to double |
| */ |
| double int64ToDoubleSafeConvert(int64_t value); |
| |
| /** |
| * @brief Gets type from templated type |
| * |
| * @tparam T Type to get name of |
| * @return std::string Name of the type |
| */ |
| template <typename T> |
| std::string typeName() |
| { |
| auto mangled = typeid(T).name(); |
| int status = 0; |
| auto demangled = abi::__cxa_demangle(mangled, nullptr, nullptr, &status); |
| std::string type = (status == 0 && demangled) ? demangled : mangled; |
| // Free memory allocated by abi::__cxa_demangle |
| std::free(demangled); |
| return type; |
| } |
| |
| template <typename T> |
| std::optional<T> |
| getPropertyFromCollection(const PropertyValuesCollection& collection, |
| const std::string& name) |
| { |
| auto fit = std::lower_bound(collection.cbegin(), collection.cend(), name, |
| [&](const auto& elem, const std::string& name) { |
| return elem.first < name; |
| }); |
| |
| if (fit == collection.cend() || fit->first != name) |
| { |
| lg2::error("getPropertyFromCollection : Property {PROP} not found.", |
| "PROP", name); |
| |
| return std::nullopt; |
| } |
| |
| return std::get<T>(fit->second); |
| } |
| |
| /** |
| * @brief Combines device type and role into a single identifier |
| * High byte (bits 15-8): Device Role |
| * Low byte (bits 7-0): Device Type |
| * |
| * @param deviceType The NSM device type (NsmDeviceIdentification) |
| * @param deviceRole The role specific to the device type |
| * @return uint16_t Combined device type and role |
| */ |
| uint16_t combineDeviceTypeAndRole(uint8_t deviceType, uint8_t deviceRole); |
| |
| /** |
| * @brief Extracts device type and role from combined identifier |
| * High byte (bits 15-8): Device Role |
| * Low byte (bits 7-0): Device Type |
| * |
| * @param combined The combined device type and role value |
| * @param deviceType Pointer to store the extracted device type (low byte) |
| * @param deviceRole Pointer to store the extracted device role (high byte) |
| */ |
| void getDeviceTypeAndRole(uint16_t combined, uint8_t* deviceType, |
| uint8_t* deviceRole); |
| |
| /** |
| * @brief Converts a MAC address to a string representation. |
| * |
| * @param macAddress Pointer to the MAC address to convert. |
| * @param macAddressDataLen Length of the MAC address. |
| * @param macAddressString Reference to the string to store the converted MAC |
| * address. |
| */ |
| void convertMacAddressToString(const uint8_t* macAddress, |
| size_t macAddressDataLen, |
| std::string& macAddressString); |
| |
| /** |
| * @brief Converts a 64-bit GUID to a string representation. |
| * |
| * @param guid The 64-bit GUID to convert. |
| * @param guidString Reference to the string to store the converted GUID. |
| */ |
| void convertGuid64ToString(uint64_t guid, std::string& guidString); |
| } // namespace utils |