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