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