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