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