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