xref: /openbmc/pldm/common/instance_id.hpp (revision f9090f3727a1a7d25c0a29b2c612fe5e3f16689a)
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