1 #pragma once 2 3 #include <libpldm/instance-id.h> 4 5 #include <phosphor-logging/lg2.hpp> 6 7 #include <cerrno> 8 #include <cstdint> 9 #include <exception> 10 #include <expected> 11 #include <string> 12 #include <system_error> 13 14 PHOSPHOR_LOG2_USING; 15 16 namespace pldm 17 { 18 19 /** 20 * @class InstanceIdError 21 * @brief Exception for PLDM instance ID allocation and management errors. 22 */ 23 class InstanceIdError : public std::exception 24 { 25 public: 26 InstanceIdError(const InstanceIdError&) noexcept = default; 27 InstanceIdError(InstanceIdError&&) noexcept = default; 28 InstanceIdError& operator=(const InstanceIdError&) noexcept = default; 29 InstanceIdError& operator=(InstanceIdError&&) noexcept = default; 30 ~InstanceIdError() noexcept override = default; 31 32 /** @brief Construct with an error code. */ 33 explicit InstanceIdError(int rc) : rc_(rc), msg_(rcToMsg(rc)) {} 34 35 /** @brief Construct with an error code and a string message (copied). */ 36 InstanceIdError(int rc, const std::string& m) : rc_(rc), msg_(m) {} 37 38 /** @brief Construct with an error code and a string message (moved). */ 39 InstanceIdError(int rc, std::string&& m) : rc_(rc), msg_(std::move(m)) {} 40 41 /** @brief Get the error code. */ 42 int rc() const noexcept 43 { 44 return rc_; 45 } 46 47 /** @brief Get the error message. */ 48 const std::string& msg() const noexcept 49 { 50 return msg_; 51 } 52 53 /** @brief Convert an error code to a message. */ 54 static std::string rcToMsg(int rc) 55 { 56 switch (rc) 57 { 58 case -EAGAIN: 59 return "No free instance ids"; 60 default: 61 return std::system_category().message(rc); 62 } 63 } 64 65 /** @brief Get the error message (for std::exception). */ 66 const char* what() const noexcept override 67 { 68 return msg_.c_str(); 69 } 70 71 private: 72 int rc_; 73 std::string msg_; 74 }; 75 76 /** @class InstanceId 77 * @brief Implementation of PLDM instance id as per DSP0240 v1.0.0 78 */ 79 class InstanceIdDb 80 { 81 public: 82 InstanceIdDb() 83 { 84 int rc = pldm_instance_db_init_default(&pldmInstanceIdDb); 85 if (rc) 86 { 87 throw std::system_category().default_error_condition(rc); 88 } 89 } 90 91 /** @brief Constructor 92 * 93 * @param[in] path - instance ID database path 94 */ 95 InstanceIdDb(const std::string& path) 96 { 97 int rc = pldm_instance_db_init(&pldmInstanceIdDb, path.c_str()); 98 if (rc) 99 { 100 throw std::system_category().default_error_condition(rc); 101 } 102 } 103 104 ~InstanceIdDb() 105 { 106 /* 107 * Abandon error-reporting. We shouldn't throw an exception from the 108 * destructor, and the class has multiple consumers using incompatible 109 * logging strategies. 110 * 111 * Broadly, it should be possible to use strace to investigate. 112 */ 113 pldm_instance_db_destroy(pldmInstanceIdDb); 114 } 115 116 /** @brief Allocate an instance ID for the given terminus 117 * @param[in] tid - the terminus ID the instance ID is associated with 118 * @return - PLDM instance id on success, or InstanceIdError on failure 119 */ 120 std::expected<uint8_t, InstanceIdError> next(uint8_t tid) 121 { 122 uint8_t id; 123 int rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid, &id); 124 125 if (rc) 126 { 127 std::string msg = 128 "Failed to allocate instance ID for EID " + 129 std::to_string(tid) + ": " + InstanceIdError::rcToMsg(rc); 130 error("Instance ID allocation failed for EID {EID}: {MSG}", "EID", 131 tid, "MSG", msg); 132 return std::unexpected(InstanceIdError{rc, std::move(msg)}); 133 } 134 135 return id; 136 } 137 138 /** @brief Mark an instance id as unused 139 * @param[in] tid - the terminus ID the instance ID is associated with 140 * @param[in] instanceId - PLDM instance id to be freed 141 */ 142 void free(uint8_t tid, uint8_t instanceId) 143 { 144 int rc = pldm_instance_id_free(pldmInstanceIdDb, tid, instanceId); 145 if (rc == -EINVAL) 146 { 147 throw std::runtime_error( 148 "Instance ID " + std::to_string(instanceId) + " for TID " + 149 std::to_string(tid) + " was not previously allocated"); 150 } 151 if (rc) 152 { 153 throw std::system_category().default_error_condition(rc); 154 } 155 } 156 157 private: 158 pldm_instance_db* pldmInstanceIdDb = nullptr; 159 }; 160 161 } // namespace pldm 162