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