1 #pragma once
2 
3 #include "Utils.hpp"
4 
5 #include <boost/asio/steady_timer.hpp>
6 #include <sdbusplus/asio/connection.hpp>
7 #include <sdbusplus/bus/match.hpp>
8 #include <sdbusplus/message.hpp>
9 #include <sdbusplus/message/native_types.hpp>
10 
11 #include <cstdint>
12 #include <iostream>
13 
14 /**
15  * @file
16  * @brief Abstract and concrete classes representing MCTP concepts and
17  *        behaviours.
18  */
19 
20 /**
21  * @brief An exception type that may be thrown by implementations of the MCTP
22  * abstract classes.
23  *
24  * This exception should be the basis for all exceptions thrown out of the MCTP
25  * APIs, and should capture any other exceptions that occur.
26  */
27 class MCTPException : public std::exception
28 {
29   public:
30     MCTPException() = delete;
MCTPException(const char * desc)31     explicit MCTPException(const char* desc) : desc(desc) {}
what() const32     const char* what() const noexcept override
33     {
34         return desc;
35     }
36 
37   private:
38     const char* desc;
39 };
40 
41 /**
42  * @brief An enum of the MCTP transports described in DSP0239 v1.10.0 Section 7.
43  *
44  * https://www.dmtf.org/sites/default/files/standards/documents/DSP0239_1.10.0.pdf
45  */
46 enum class MCTPTransport
47 {
48     Reserved = 0x00,
49     SMBus = 0x01,
50 };
51 
52 /**
53  * @brief Captures properties of MCTP interfaces.
54  *
55  * https://github.com/CodeConstruct/mctp/blob/v1.1/src/mctp.c#L672-L703
56  */
57 struct MCTPInterface
58 {
59     std::string name;
60     MCTPTransport transport;
61 
62     auto operator<=>(const MCTPInterface& r) const = default;
63 };
64 
65 class MCTPDevice;
66 
67 /**
68  * @brief Captures the behaviour of an endpoint at the MCTP layer
69  *
70  * The lifetime of an instance of MctpEndpoint is proportional to the lifetime
71  * of the endpoint configuration. If an endpoint is deconfigured such that its
72  * device has no assigned EID, then any related MctpEndpoint instance must be
73  * destructed as a consequence.
74  */
75 class MCTPEndpoint
76 {
77   public:
78     using Event = std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)>;
79     using Result = std::function<void(const std::error_code& ec)>;
80 
81     virtual ~MCTPEndpoint() = default;
82 
83     /**
84      * @return The Linux network ID of the network in which the endpoint
85                participates
86      */
87     virtual int network() const = 0;
88 
89     /**
90      * @return The MCTP endpoint ID of the endpoint in its network
91      */
92     virtual uint8_t eid() const = 0;
93 
94     /**
95      * @brief Subscribe to events produced by an endpoint object across its
96      *        lifecycle
97      *
98      * @param degraded The callback to execute when the MCTP layer indicates the
99      *                 endpoint is unresponsive
100      *
101      * @param available The callback to execute when the MCTP layer indicates
102      *                  that communication with the degraded endpoint has been
103      *                  recovered
104      *
105      * @param removed The callback to execute when the MCTP layer indicates the
106      *                endpoint has been removed.
107      */
108     virtual void subscribe(Event&& degraded, Event&& available,
109                            Event&& removed) = 0;
110 
111     /**
112      * @brief Remove the endpoint from its associated network
113      */
114     virtual void remove() = 0;
115 
116     /**
117      * @return A formatted string representing the endpoint in terms of its
118      *         address properties
119      */
120     virtual std::string describe() const = 0;
121 
122     /**
123      * @return A shared pointer to the device instance associated with the
124      *         endpoint.
125      */
126     virtual std::shared_ptr<MCTPDevice> device() const = 0;
127 };
128 
129 /**
130  * @brief Represents an MCTP-capable device on a bus.
131  *
132  * It is often known that an MCTP-capable device exists on a bus prior to the
133  * MCTP stack configuring the device for communication. MctpDevice exposes the
134  * ability to set-up the endpoint device for communication.
135  *
136  * The lifetime of an MctpDevice instance is proportional to the existence of an
137  * MCTP-capable device in the system. If a device represented by an MctpDevice
138  * instance is removed from the system then any related MctpDevice instance must
139  * be destructed a consequence.
140  *
141  * Successful set-up of the device as an endpoint yields an MctpEndpoint
142  * instance. The lifetime of the MctpEndpoint instance produced must not exceed
143  * the lifetime of its parent MctpDevice.
144  */
145 class MCTPDevice
146 {
147   public:
148     virtual ~MCTPDevice() = default;
149 
150     /**
151      * @brief Configure the device for MCTP communication
152      *
153      * @param added The callback to invoke once the setup process has
154      *              completed. The provided error code @p ec must be
155      *              checked as the request may not have succeeded. If
156      *              the request was successful then @p ep contains a
157      *              valid MctpEndpoint instance.
158      */
159     virtual void
160         setup(std::function<void(const std::error_code& ec,
161                                  const std::shared_ptr<MCTPEndpoint>& ep)>&&
162                   added) = 0;
163 
164     /**
165      * @brief Remove the device and any associated endpoint from the MCTP stack.
166      */
167     virtual void remove() = 0;
168 
169     /**
170      * @return A formatted string representing the device in terms of its
171      *         address properties.
172      */
173     virtual std::string describe() const = 0;
174 };
175 
176 class MCTPDDevice;
177 
178 /**
179  * @brief An implementation of MctpEndpoint in terms of the D-Bus interfaces
180  *        exposed by @c mctpd.
181  *
182  * The lifetime of an MctpdEndpoint is proportional to the lifetime of the
183  * endpoint object exposed by @c mctpd. The lifecycle of @c mctpd endpoint
184  * objects is discussed here:
185  *
186  * https://github.com/CodeConstruct/mctp/pull/23/files#diff-00234f5f2543b8b9b8a419597e55121fe1cc57cf1c7e4ff9472bed83096bd28e
187  */
188 class MCTPDEndpoint :
189     public MCTPEndpoint,
190     public std::enable_shared_from_this<MCTPDEndpoint>
191 {
192   public:
193     static std::string path(const std::shared_ptr<MCTPEndpoint>& ep);
194 
195     MCTPDEndpoint() = delete;
MCTPDEndpoint(const std::shared_ptr<MCTPDDevice> & dev,const std::shared_ptr<sdbusplus::asio::connection> & connection,sdbusplus::message::object_path objpath,int network,uint8_t eid)196     MCTPDEndpoint(
197         const std::shared_ptr<MCTPDDevice>& dev,
198         const std::shared_ptr<sdbusplus::asio::connection>& connection,
199         sdbusplus::message::object_path objpath, int network, uint8_t eid) :
200         dev(dev), connection(connection), objpath(std::move(objpath)),
201         mctp{network, eid}
202     {}
203     MCTPDEndpoint& McptdEndpoint(const MCTPDEndpoint& other) = delete;
204     MCTPDEndpoint(MCTPDEndpoint&& other) noexcept = default;
205     ~MCTPDEndpoint() override = default;
206 
207     int network() const override;
208     uint8_t eid() const override;
209     void subscribe(Event&& degraded, Event&& available,
210                    Event&& removed) override;
211     void remove() override;
212 
213     std::string describe() const override;
214 
215     std::shared_ptr<MCTPDevice> device() const override;
216 
217     /**
218      * @brief Indicate the endpoint has been removed
219      *
220      * Called from the implementation of MctpdDevice for resource cleanup
221      * prior to destruction. Resource cleanup is delegated by invoking the
222      * notifyRemoved() callback. As the actions may be abitrary we avoid
223      * invoking notifyRemoved() in the destructor.
224      */
225     void removed();
226 
227   private:
228     std::shared_ptr<MCTPDDevice> dev;
229     std::shared_ptr<sdbusplus::asio::connection> connection;
230     sdbusplus::message::object_path objpath;
231     struct
232     {
233         int network;
234         uint8_t eid;
235     } mctp;
236     MCTPEndpoint::Event notifyAvailable;
237     MCTPEndpoint::Event notifyDegraded;
238     MCTPEndpoint::Event notifyRemoved;
239     std::optional<sdbusplus::bus::match_t> connectivityMatch;
240 
241     void onMctpEndpointChange(sdbusplus::message_t& msg);
242     void updateEndpointConnectivity(const std::string& connectivity);
243 };
244 
245 /**
246  * @brief An implementation of MctpDevice in terms of D-Bus interfaces exposed
247  *        by @c mctpd.
248  *
249  * The construction or destruction of an MctpdDevice is not required to be
250  * correlated with signals from @c mctpd. For instance, EntityManager may expose
251  * the existance of an MCTP-capable device through its usual configuration
252  * mechanisms.
253  */
254 class MCTPDDevice :
255     public MCTPDevice,
256     public std::enable_shared_from_this<MCTPDDevice>
257 {
258   public:
259     MCTPDDevice() = delete;
260     MCTPDDevice(const std::shared_ptr<sdbusplus::asio::connection>& connection,
261                 const std::string& interface,
262                 const std::vector<uint8_t>& physaddr);
263     MCTPDDevice(const MCTPDDevice& other) = delete;
264     MCTPDDevice(MCTPDDevice&& other) = delete;
265     ~MCTPDDevice() override = default;
266 
267     void setup(std::function<void(const std::error_code& ec,
268                                   const std::shared_ptr<MCTPEndpoint>& ep)>&&
269                    added) override;
270     void remove() override;
271     std::string describe() const override;
272 
273   private:
274     static void onEndpointInterfacesRemoved(
275         const std::weak_ptr<MCTPDDevice>& weak, const std::string& objpath,
276         sdbusplus::message_t& msg);
277 
278     std::shared_ptr<sdbusplus::asio::connection> connection;
279     const std::string interface;
280     const std::vector<uint8_t> physaddr;
281     std::shared_ptr<MCTPDEndpoint> endpoint;
282     std::unique_ptr<sdbusplus::bus::match_t> removeMatch;
283 
284     /**
285      * @brief Actions to perform once endpoint setup has succeeded
286      *
287      * Now that the endpoint exists two tasks remain:
288      *
289      * 1. Setup the match capturing removal of the endpoint object by mctpd
290      * 2. Invoke the callback to notify the requester that setup has completed,
291      *    providing the MctpEndpoint instance associated with the MctpDevice.
292      */
293     void finaliseEndpoint(
294         const std::string& objpath, uint8_t eid, int network,
295         std::function<void(const std::error_code& ec,
296                            const std::shared_ptr<MCTPEndpoint>& ep)>& added);
297     void endpointRemoved();
298 };
299 
300 class I2CMCTPDDevice : public MCTPDDevice
301 {
302   public:
303     static std::optional<SensorBaseConfigMap> match(const SensorData& config);
304     static bool match(const std::set<std::string>& interfaces);
305     static std::shared_ptr<I2CMCTPDDevice>
306         from(const std::shared_ptr<sdbusplus::asio::connection>& connection,
307              const SensorBaseConfigMap& iface);
308 
309     I2CMCTPDDevice() = delete;
I2CMCTPDDevice(const std::shared_ptr<sdbusplus::asio::connection> & connection,int bus,uint8_t physaddr)310     I2CMCTPDDevice(
311         const std::shared_ptr<sdbusplus::asio::connection>& connection, int bus,
312         uint8_t physaddr) :
313         MCTPDDevice(connection, interfaceFromBus(bus), {physaddr})
314     {}
315     ~I2CMCTPDDevice() override = default;
316 
317   private:
318     static constexpr const char* configType = "MCTPI2CTarget";
319 
320     static std::string interfaceFromBus(int bus);
321 };
322