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 boardcast 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 */ 91 constexpr uint8_t ipmbNetFnLunSet(uint8_t netFn, uint8_t lun) 92 { 93 return ((netFn << 2) | (lun & ipmbLunMask)); 94 } 95 96 constexpr uint8_t ipmbSeqLunSet(uint8_t seq, uint8_t lun) 97 { 98 return ((seq << 2) | (lun & ipmbLunMask)); 99 } 100 101 constexpr uint8_t ipmbAddressTo7BitSet(uint8_t address) 102 { 103 return address >> 1; 104 } 105 106 constexpr uint8_t ipmbRespNetFn(uint8_t netFn) 107 { 108 return netFn |= 1; 109 } 110 111 /** 112 * @brief Ipmb getters 113 */ 114 constexpr uint8_t ipmbNetFnGet(uint8_t netFnLun) 115 { 116 return netFnLun >> 2; 117 } 118 119 constexpr uint8_t ipmbLunFromNetFnLunGet(uint8_t netFnLun) 120 { 121 return netFnLun & ipmbLunMask; 122 } 123 124 constexpr uint8_t ipmbSeqGet(uint8_t seqNumLun) 125 { 126 return seqNumLun >> 2; 127 } 128 129 constexpr uint8_t ipmbLunFromSeqLunGet(uint8_t seqNumLun) 130 { 131 return seqNumLun & ipmbLunMask; 132 } 133 134 /** 135 * @brief Ipmb checkers 136 */ 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 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: 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 343 int name() 344 { 345 return static_cast<int>(I2C_RDWR); 346 } 347 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