xref: /openbmc/ipmbbridge/ipmbbridged.hpp (revision 524f753f)
1 /* Copyright 2018 Intel
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *    http://www.apache.org/licenses/LICENSE-2.0
8  *
9  *  Unless required by applicable law or agreed to in writing, software
10  *  distributed under the License is distributed on an "AS IS" BASIS,
11  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  *  See the License for the specific language governing permissions and
13  *  limitations under the License.
14  */
15 
16 #include "ipmbdefines.hpp"
17 
18 #include <boost/asio/io_context.hpp>
19 #include <boost/asio/steady_timer.hpp>
20 #include <boost/container/flat_set.hpp>
21 #include <sdbusplus/asio/object_server.hpp>
22 #include <sdbusplus/message.hpp>
23 
24 #include <optional>
25 #include <vector>
26 
27 extern "C"
28 {
29 #include <i2c/smbus.h>
30 #include <linux/i2c-dev.h>
31 }
32 
33 #ifndef IPMBBRIDGED_HPP
34 #define IPMBBRIDGED_HPP
35 
36 /**
37  * @brief Ipmb return status codes (sendRequest API call)
38  */
39 enum class ipmbResponseStatus
40 {
41     success = 0,
42     error = 1,
43     invalid_param = 2,
44     busy = 3,
45     timeout = 4,
46 };
47 
48 /**
49  * @brief Ipmb outstanding requests defines
50  */
51 constexpr int ipmbMaxOutstandingRequestsCount = 64;
52 constexpr int ipmbNumberOfTries = 6;
53 constexpr uint64_t ipmbRequestRetryTimeout = 250; // ms
54 
55 /**
56  * @brief Ipmb I2C communication
57  */
58 constexpr uint8_t ipmbI2cNumberOfRetries = 2;
59 
60 /**
61  * @brief Ipmb broadcast address
62  */
63 constexpr uint8_t broadcastAddress = 0x0;
64 
65 /**
66  * @brief Ipmb defines
67  */
68 constexpr size_t ipmbMaxDataSize = 256;
69 constexpr size_t ipmbConnectionHeaderLength = 3;
70 constexpr size_t ipmbResponseDataHeaderLength = 4;
71 constexpr size_t ipmbRequestDataHeaderLength = 3;
72 constexpr size_t ipmbAddressSize = 1;
73 constexpr size_t ipmbPktLenSize = 1;
74 constexpr size_t ipmbChecksumSize = 1;
75 constexpr size_t ipmbChecksum2StartOffset = 3;
76 constexpr ssize_t ipmbMinFrameLength = 7;
77 constexpr ssize_t ipmbMaxFrameLength =
78     ipmbPktLenSize + ipmbConnectionHeaderLength + ipmbResponseDataHeaderLength +
79     ipmbChecksumSize + ipmbMaxDataSize;
80 
81 /**
82  * @brief Ipmb misc
83  */
84 constexpr uint8_t ipmbNetFnResponseMask = 0x01;
85 constexpr uint8_t ipmbLunMask = 0x03;
86 constexpr uint8_t ipmbRsLun = 0x0;
87 
88 /**
89  * @brief Ipmb setters
90  */
ipmbNetFnLunSet(uint8_t netFn,uint8_t lun)91 constexpr uint8_t ipmbNetFnLunSet(uint8_t netFn, uint8_t lun)
92 {
93     return ((netFn << 2) | (lun & ipmbLunMask));
94 }
95 
ipmbSeqLunSet(uint8_t seq,uint8_t lun)96 constexpr uint8_t ipmbSeqLunSet(uint8_t seq, uint8_t lun)
97 {
98     return ((seq << 2) | (lun & ipmbLunMask));
99 }
100 
ipmbAddressTo7BitSet(uint8_t address)101 constexpr uint8_t ipmbAddressTo7BitSet(uint8_t address)
102 {
103     return address >> 1;
104 }
105 
ipmbRespNetFn(uint8_t netFn)106 constexpr uint8_t ipmbRespNetFn(uint8_t netFn)
107 {
108     return netFn |= 1;
109 }
110 
111 /**
112  * @brief Ipmb getters
113  */
ipmbNetFnGet(uint8_t netFnLun)114 constexpr uint8_t ipmbNetFnGet(uint8_t netFnLun)
115 {
116     return netFnLun >> 2;
117 }
118 
ipmbLunFromNetFnLunGet(uint8_t netFnLun)119 constexpr uint8_t ipmbLunFromNetFnLunGet(uint8_t netFnLun)
120 {
121     return netFnLun & ipmbLunMask;
122 }
123 
ipmbSeqGet(uint8_t seqNumLun)124 constexpr uint8_t ipmbSeqGet(uint8_t seqNumLun)
125 {
126     return seqNumLun >> 2;
127 }
128 
ipmbLunFromSeqLunGet(uint8_t seqNumLun)129 constexpr uint8_t ipmbLunFromSeqLunGet(uint8_t seqNumLun)
130 {
131     return seqNumLun & ipmbLunMask;
132 }
133 
134 /**
135  * @brief Ipmb checkers
136  */
ipmbIsResponse(IPMB_HEADER * ipmbHeader)137 constexpr bool ipmbIsResponse(IPMB_HEADER* ipmbHeader)
138 {
139     return ipmbNetFnGet(ipmbHeader->Header.Resp.rqNetFnLUN) &
140            ipmbNetFnResponseMask;
141 }
142 
143 /**
144  * @brief Ipmb request state
145  */
146 enum class ipmbRequestState
147 {
148     invalid,
149     valid,
150     matched,
151 };
152 
153 /**
154  * @brief Channel types
155  */
156 enum class ipmbChannelType
157 {
158     ipmb = 0,
159     me = 1
160 };
161 
162 /**
163  * @brief IpmbResponse declaration
164  */
165 struct IpmbResponse
166 {
167     uint8_t address;
168     uint8_t netFn;
169     uint8_t rqLun;
170     uint8_t rsSA;
171     uint8_t seq;
172     uint8_t rsLun;
173     uint8_t cmd;
174     uint8_t completionCode;
175     std::vector<uint8_t> data;
176 
177     IpmbResponse();
178 
179     IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun, uint8_t rsSA,
180                  uint8_t seq, uint8_t rsLun, uint8_t cmd,
181                  uint8_t completionCode, const std::vector<uint8_t>& inputData);
182 
183     void i2cToIpmbConstruct(IPMB_HEADER* ipmbBuffer, size_t bufferLength);
184 
185     std::shared_ptr<std::vector<uint8_t>> ipmbToi2cConstruct();
186 };
187 
188 /**
189  * @brief IpmbRequest declaration
190  */
191 struct IpmbRequest
192 {
193     uint8_t address;
194     uint8_t netFn;
195     uint8_t rsLun;
196     uint8_t rqSA;
197     uint8_t seq;
198     uint8_t rqLun;
199     uint8_t cmd;
200     std::vector<uint8_t> data;
201 
202     size_t dataLength;
203     ipmbRequestState state;
204     std::optional<boost::asio::steady_timer> timer;
205     std::unique_ptr<IpmbResponse> matchedResponse;
206 
207     // creates empty request with empty timer object
208     IpmbRequest();
209 
210     IpmbRequest(uint8_t address, uint8_t netFn, uint8_t rsLun, uint8_t rqSA,
211                 uint8_t seq, uint8_t rqLun, uint8_t cmd,
212                 const std::vector<uint8_t>& inputData);
213 
214     IpmbRequest(const IpmbRequest&) = delete;
215     IpmbRequest& operator=(const IpmbRequest&) = delete;
216 
217     std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
218         returnMatchedResponse();
219 
220     std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
221         returnStatusResponse(int status);
222 
223     void i2cToIpmbConstruct(IPMB_HEADER* ipmbBuffer, size_t bufferLength);
224 
225     int ipmbToi2cConstruct(std::vector<uint8_t>& buffer);
226 };
227 
228 /**
229  * @brief Command filtering class declaration
230  *
231  * This feature provides simple mechanism for filtering out commands - which are
232  * not implemented in IPMI - on IPMB level, in order to reduce DBus traffic
233  */
234 class IpmbCommandFilter
235 {
236   public:
237     // function checking if netFn & cmd combination exist in blocked command
238     // list
239     bool isBlocked(const uint8_t reqNetFn, const uint8_t cmd);
240     // function adding netfFn & cmd combination to the blocked command list
241     void addFilter(const uint8_t reqNetFn, const uint8_t cmd);
242 
243   private:
244     boost::container::flat_set<std::pair<uint8_t, uint8_t>> unhandledCommands;
245 };
246 
247 /**
248  * @brief Command filtering defines
249  */
250 
251 constexpr uint8_t ipmbIpmiInvalidCmd = 0xC1;
252 constexpr uint8_t ipmbIpmiCmdRespNotProvided = 0xCE;
253 
ipmbReqNetFnFromRespNetFn(uint8_t reqNetFn)254 constexpr uint8_t ipmbReqNetFnFromRespNetFn(uint8_t reqNetFn)
255 {
256     return reqNetFn & ~ipmbNetFnResponseMask;
257 }
258 
259 /**
260  * @brief IpmbChannel class declaration
261  */
262 class IpmbChannel
263 {
264   public:
265     IpmbChannel(boost::asio::io_context& io, uint8_t ipmbBmcTargetAddress,
266                 uint8_t ipmbRqTargetAddress, uint8_t channelIdx,
267                 std::shared_ptr<IpmbCommandFilter> commandFilter);
268 
269     IpmbChannel(const IpmbChannel&) = delete;
270     IpmbChannel& operator=(const IpmbChannel&) = delete;
271 
272     int ipmbChannelInit(const char* ipmbI2cTarget);
273 
274     int ipmbChannelUpdateTargetAddress(const uint8_t newBmcTargetAddr);
275 
276     bool seqNumGet(uint8_t& seq);
277 
278     ipmbChannelType getChannelType();
279 
280     uint8_t getBusId();
281 
282     uint8_t getDevIndex();
283 
284     uint8_t getChannelIdx();
285 
286     uint8_t getBmcTargetAddress();
287 
288     uint8_t getRqTargetAddress();
289 
290     void addFilter(const uint8_t respNetFn, const uint8_t cmd);
291 
292     void processI2cEvent();
293 
294     void ipmbSendI2cFrame(std::shared_ptr<std::vector<uint8_t>> buffer,
295                           size_t retriesAttempted);
296 
297     std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
298         requestAdd(boost::asio::yield_context& yield,
299                    std::shared_ptr<IpmbRequest> requestToSend);
300 
301   private:
302     boost::asio::posix::stream_descriptor i2cTargetDescriptor;
303 
304     int ipmbi2cTargetFd;
305 
306     uint8_t ipmbBmcTargetAddress;
307     uint8_t ipmbRqTargetAddress;
308     uint8_t ipmbBusId;
309     uint8_t channelIdx;
310 
311     std::shared_ptr<IpmbCommandFilter> commandFilter;
312 
313     // array storing outstanding requests
314     std::array<std::shared_ptr<IpmbRequest>, ipmbMaxOutstandingRequestsCount>
315         outstandingRequests;
316 
317     void requestTimerCallback(std::shared_ptr<IpmbRequest> request,
318                               std::shared_ptr<std::vector<uint8_t>> buffer);
319 
320     void responseMatch(std::unique_ptr<IpmbResponse>& response);
321 
322     void makeRequestInvalid(IpmbRequest& request);
323 
324     void makeRequestValid(std::shared_ptr<IpmbRequest> request);
325 };
326 
327 /**
328  * @brief ioWrite class declaration
329  */
330 class ioWrite
331 {
332   public:
ioWrite(std::vector<uint8_t> & buffer)333     ioWrite(std::vector<uint8_t>& buffer)
334     {
335         i2cmsg[0].addr = ipmbAddressTo7BitSet(buffer[0]);
336         i2cmsg[0].len = buffer.size() - ipmbAddressSize;
337         i2cmsg[0].buf = buffer.data() + ipmbAddressSize;
338 
339         msgRdwr.msgs = i2cmsg;
340         msgRdwr.nmsgs = 1;
341     };
342 
name()343     int name()
344     {
345         return static_cast<int>(I2C_RDWR);
346     }
347 
data()348     void* data()
349     {
350         return &msgRdwr;
351     }
352 
353   private:
354     i2c_rdwr_ioctl_data msgRdwr = {};
355     i2c_msg i2cmsg[1] = {};
356 };
357 
358 #endif
359