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 "ipmbbridged.hpp" 17 18 #include "ipmbdefines.hpp" 19 #include "ipmbutils.hpp" 20 21 #include <boost/algorithm/string/replace.hpp> 22 #include <boost/asio/io_context.hpp> 23 #include <boost/asio/write.hpp> 24 #include <nlohmann/json.hpp> 25 #include <phosphor-logging/log.hpp> 26 27 #include <filesystem> 28 #include <fstream> 29 #include <list> 30 #include <tuple> 31 #include <unordered_map> 32 33 /** 34 * @brief Dbus 35 */ 36 static constexpr const char* ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb"; 37 static constexpr const char* ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb"; 38 static constexpr const char* ipmbDbusIntf = "org.openbmc.Ipmb"; 39 40 boost::asio::io_context io; 41 auto conn = std::make_shared<sdbusplus::asio::connection>(io); 42 43 static std::list<IpmbChannel> ipmbChannels; 44 static const std::unordered_map<std::string, ipmbChannelType> 45 ipmbChannelTypeMap = {{"me", ipmbChannelType::me}, 46 {"ipmb", ipmbChannelType::ipmb}}; 47 48 /** 49 * @brief Ipmb request class methods 50 */ 51 IpmbRequest::IpmbRequest() 52 { 53 data.reserve(ipmbMaxDataSize); 54 } 55 56 IpmbRequest::IpmbRequest(uint8_t address, uint8_t netFn, uint8_t rsLun, 57 uint8_t rqSA, uint8_t seq, uint8_t rqLun, uint8_t cmd, 58 const std::vector<uint8_t>& inputData) : 59 address(address), netFn(netFn), rsLun(rsLun), rqSA(rqSA), seq(seq), 60 rqLun(rqLun), cmd(cmd), timer(io) 61 { 62 data.reserve(ipmbMaxDataSize); 63 state = ipmbRequestState::invalid; 64 65 if (inputData.size() > 0) 66 { 67 data = std::move(inputData); 68 } 69 } 70 71 void IpmbRequest::i2cToIpmbConstruct(IPMB_HEADER* ipmbBuffer, 72 size_t bufferLength) 73 { 74 // constructing ipmb request from i2c buffer 75 netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN); 76 rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN); 77 rqSA = ipmbBuffer->Header.Req.rqSA; 78 seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN); 79 rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN); 80 cmd = ipmbBuffer->Header.Req.cmd; 81 82 size_t dataLength = 83 bufferLength - (ipmbConnectionHeaderLength + 84 ipmbRequestDataHeaderLength + ipmbChecksumSize); 85 86 if (dataLength > 0) 87 { 88 data.insert(data.end(), ipmbBuffer->Header.Req.data, 89 &ipmbBuffer->Header.Req.data[dataLength]); 90 } 91 } 92 93 int IpmbRequest::ipmbToi2cConstruct(std::vector<uint8_t>& buffer) 94 { 95 /* Add one byte for length byte as per required by driver */ 96 size_t bufferLength = 1 + data.size() + ipmbRequestDataHeaderLength + 97 ipmbConnectionHeaderLength + ipmbChecksumSize; 98 99 if (bufferLength > ipmbMaxFrameLength) 100 { 101 return -1; 102 } 103 104 buffer.resize(bufferLength); 105 static_assert(ipmbMaxFrameLength >= sizeof(IPMB_HEADER)); 106 IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer.data()); 107 ipmbPkt->len = bufferLength - 1; 108 IPMB_HEADER* ipmbBuffer = &(ipmbPkt->hdr); 109 110 // constructing buffer from ipmb request 111 ipmbBuffer->Header.Req.address = address; 112 ipmbBuffer->Header.Req.rsNetFnLUN = ipmbNetFnLunSet(netFn, rsLun); 113 ipmbBuffer->Header.Req.rqSA = rqSA; 114 ipmbBuffer->Header.Req.rqSeqLUN = ipmbSeqLunSet(seq, rqLun); 115 ipmbBuffer->Header.Req.cmd = cmd; 116 117 ipmbBuffer->Header.Req.checksum1 = ipmbChecksumCompute( 118 (uint8_t*)ipmbBuffer, ipmbConnectionHeaderLength - ipmbChecksumSize); 119 120 if (data.size() > 0) 121 { 122 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Req.data); 123 } 124 125 buffer[bufferLength - ipmbChecksumSize] = 126 ipmbChecksumCompute((uint8_t*)ipmbBuffer + ipmbChecksum2StartOffset, 127 (ipmbRequestDataHeaderLength + data.size())); 128 129 return 0; 130 } 131 132 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> 133 IpmbRequest::returnMatchedResponse() 134 { 135 return std::make_tuple( 136 static_cast<int>(ipmbResponseStatus::success), matchedResponse->netFn, 137 matchedResponse->rsLun, matchedResponse->cmd, 138 matchedResponse->completionCode, matchedResponse->data); 139 } 140 141 static std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> 142 returnStatus(ipmbResponseStatus status) 143 { 144 // we only want to send status here, other fields are not relevant 145 return std::make_tuple(static_cast<int>(status), 0, 0, 0, 0, 146 std::vector<uint8_t>(0)); 147 } 148 149 /** 150 * @brief Ipmb response class methods 151 */ 152 IpmbResponse::IpmbResponse() 153 { 154 data.reserve(ipmbMaxDataSize); 155 } 156 157 IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun, 158 uint8_t rsSA, uint8_t seq, uint8_t rsLun, 159 uint8_t cmd, uint8_t completionCode, 160 const std::vector<uint8_t>& inputData) : 161 address(address), netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), 162 rsLun(rsLun), cmd(cmd), completionCode(completionCode) 163 { 164 data.reserve(ipmbMaxDataSize); 165 166 if (inputData.size() > 0) 167 { 168 data = std::move(inputData); 169 } 170 } 171 172 void IpmbResponse::i2cToIpmbConstruct(IPMB_HEADER* ipmbBuffer, 173 size_t bufferLength) 174 { 175 netFn = ipmbNetFnGet(ipmbBuffer->Header.Resp.rqNetFnLUN); 176 rqLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Resp.rqNetFnLUN); 177 rsSA = ipmbBuffer->Header.Resp.rsSA; 178 seq = ipmbSeqGet(ipmbBuffer->Header.Resp.rsSeqLUN); 179 rsLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Resp.rsSeqLUN); 180 cmd = ipmbBuffer->Header.Resp.cmd; 181 completionCode = ipmbBuffer->Header.Resp.completionCode; 182 183 size_t dataLength = 184 bufferLength - (ipmbConnectionHeaderLength + 185 ipmbResponseDataHeaderLength + ipmbChecksumSize); 186 187 if (dataLength > 0) 188 { 189 data.insert(data.end(), ipmbBuffer->Header.Resp.data, 190 &ipmbBuffer->Header.Resp.data[dataLength]); 191 } 192 } 193 194 std::shared_ptr<std::vector<uint8_t>> IpmbResponse::ipmbToi2cConstruct() 195 { 196 /* Add one byte for length byte as per required by driver */ 197 size_t bufferLength = 1 + data.size() + ipmbResponseDataHeaderLength + 198 ipmbConnectionHeaderLength + ipmbChecksumSize; 199 200 if (bufferLength > ipmbMaxFrameLength) 201 { 202 return nullptr; 203 } 204 205 std::shared_ptr<std::vector<uint8_t>> buffer = 206 std::make_shared<std::vector<uint8_t>>(bufferLength); 207 208 IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer->data()); 209 ipmbPkt->len = bufferLength - 1; 210 IPMB_HEADER* ipmbBuffer = &(ipmbPkt->hdr); 211 212 ipmbBuffer->Header.Resp.address = address; 213 ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun); 214 ipmbBuffer->Header.Resp.rsSA = rsSA; 215 ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun); 216 ipmbBuffer->Header.Resp.cmd = cmd; 217 ipmbBuffer->Header.Resp.completionCode = completionCode; 218 219 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute( 220 (uint8_t*)ipmbBuffer, ipmbConnectionHeaderLength - ipmbChecksumSize); 221 222 if (data.size() > 0) 223 { 224 std::copy(data.begin(), data.end(), ipmbBuffer->Header.Resp.data); 225 } 226 227 (*buffer)[bufferLength - ipmbChecksumSize] = 228 ipmbChecksumCompute((uint8_t*)ipmbBuffer + ipmbChecksum2StartOffset, 229 (ipmbResponseDataHeaderLength + data.size())); 230 231 return buffer; 232 } 233 234 bool IpmbCommandFilter::isBlocked(const uint8_t reqNetFn, const uint8_t cmd) 235 { 236 auto blockedCmd = unhandledCommands.find({reqNetFn, cmd}); 237 238 if (blockedCmd != unhandledCommands.end()) 239 { 240 return true; 241 } 242 243 return false; 244 } 245 246 void IpmbCommandFilter::addFilter(const uint8_t reqNetFn, const uint8_t cmd) 247 { 248 if (unhandledCommands.insert({reqNetFn, cmd}).second) 249 { 250 phosphor::logging::log<phosphor::logging::level::INFO>( 251 "addFilter: added command to filter", 252 phosphor::logging::entry("netFn = %d", reqNetFn), 253 phosphor::logging::entry("cmd = %d", cmd)); 254 } 255 } 256 257 /** 258 * @brief Ipmb channel 259 */ 260 void IpmbChannel::ipmbSendI2cFrame(std::shared_ptr<std::vector<uint8_t>> buffer, 261 size_t retriesAttempted = 0) 262 { 263 IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer->data()); 264 uint8_t targetAddr = ipmbIsResponse(&(ipmbPkt->hdr)) 265 ? ipmbPkt->hdr.Header.Resp.address 266 : ipmbPkt->hdr.Header.Req.address; 267 boost::asio::async_write( 268 i2cTargetDescriptor, boost::asio::buffer(*buffer), 269 [this, buffer, retriesAttempted, targetAddr]( 270 const boost::system::error_code& ec, size_t /* bytesSent */) { 271 if (ec) 272 { 273 size_t currentRetryCnt = retriesAttempted; 274 275 if (currentRetryCnt > ipmbI2cNumberOfRetries) 276 { 277 std::string msgToLog = 278 "ipmbSendI2cFrame: send to I2C failed after retries." 279 " busId=" + 280 std::to_string(ipmbBusId) + 281 ", targetAddr=" + std::to_string(targetAddr) + 282 ", error=" + ec.message(); 283 phosphor::logging::log<phosphor::logging::level::ERR>( 284 msgToLog.c_str()); 285 return; 286 } 287 currentRetryCnt++; 288 ipmbSendI2cFrame(buffer, currentRetryCnt); 289 } 290 }); 291 } 292 293 /** 294 * @brief Ipmb Outstanding Requests 295 */ 296 void IpmbChannel::makeRequestInvalid(IpmbRequest& request) 297 { 298 // change request state to invalid and remove it from outstanding requests 299 // list 300 request.state = ipmbRequestState::invalid; 301 outstandingRequests[request.seq] = nullptr; 302 } 303 304 void IpmbChannel::makeRequestValid(std::shared_ptr<IpmbRequest> request) 305 { 306 // change request state to valid and add it to outstanding requests list 307 request->state = ipmbRequestState::valid; 308 outstandingRequests[request->seq] = request; 309 } 310 311 bool IpmbChannel::seqNumGet(uint8_t& seq) 312 { 313 static uint8_t seqNum = 0; 314 315 for (int i = 0; i < ipmbMaxOutstandingRequestsCount; i++) 316 { 317 seqNum = (seqNum + 1) % ipmbMaxOutstandingRequestsCount; 318 319 if (outstandingRequests[seqNum] == nullptr) 320 { 321 seq = seqNum; 322 return true; 323 } 324 } 325 326 return false; 327 } 328 329 void IpmbChannel::responseMatch(std::unique_ptr<IpmbResponse>& response) 330 { 331 std::shared_ptr<IpmbRequest> request = outstandingRequests[response->seq]; 332 333 if (request != nullptr) 334 { 335 if (((ipmbRespNetFn(request->netFn)) == (response->netFn)) && 336 ((request->rqLun) == (response->rqLun)) && 337 ((request->rsLun) == (response->rsLun)) && 338 ((request->cmd) == (response->cmd))) 339 { 340 // match, response is corresponding to previously sent request 341 request->state = ipmbRequestState::matched; 342 request->timer->cancel(); 343 request->matchedResponse = std::move(response); 344 } 345 } 346 } 347 348 void IpmbChannel::processI2cEvent() 349 { 350 std::array<uint8_t, ipmbMaxFrameLength> buffer{}; 351 IPMB_PKT* ipmbPkt = reinterpret_cast<IPMB_PKT*>(buffer.data()); 352 IPMB_HEADER* ipmbFrame = &(ipmbPkt->hdr); 353 354 lseek(ipmbi2cTargetFd, 0, SEEK_SET); 355 ssize_t r = read(ipmbi2cTargetFd, buffer.data(), ipmbMaxFrameLength); 356 357 // Handle error cases. 358 if (r < 0) 359 { 360 goto end; 361 } 362 363 /* Subtract first byte len size from total frame length */ 364 r--; 365 366 if ((r < ipmbMinFrameLength) || (r > ipmbMaxFrameLength)) 367 { 368 goto end; 369 } 370 371 // validate the frame 372 if (!isFrameValid(ipmbFrame, r)) 373 { 374 goto end; 375 } 376 377 // if it is message received from ipmb channel, send out dbus signal 378 if (getChannelType() == ipmbChannelType::ipmb) 379 { 380 auto ipmbMessageReceived = IpmbRequest(); 381 ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r); 382 sdbusplus::message_t msg = 383 conn->new_signal(ipmbObj, ipmbDbusIntf, "receiveBroadcast"); 384 msg.append(ipmbMessageReceived.netFn, ipmbMessageReceived.cmd, 385 ipmbMessageReceived.data); 386 msg.signal_send(); 387 } 388 389 // copy frame to ipmib message buffer 390 if (ipmbIsResponse(ipmbFrame)) 391 { 392 std::unique_ptr<IpmbResponse> ipmbMessageReceived = 393 std::make_unique<IpmbResponse>(); 394 395 ipmbMessageReceived->i2cToIpmbConstruct(ipmbFrame, r); 396 397 // try to match response with outstanding request 398 responseMatch(ipmbMessageReceived); 399 } 400 else 401 { 402 // if command is blocked - respond with 'invalid command' 403 // completion code 404 if (commandFilter) 405 { 406 uint8_t netFn = ipmbNetFnGet(ipmbFrame->Header.Req.rsNetFnLUN); 407 uint8_t cmd = ipmbFrame->Header.Req.cmd; 408 uint8_t rqSA = ipmbFrame->Header.Req.rqSA; 409 410 if (commandFilter->isBlocked(netFn, cmd)) 411 { 412 uint8_t seq = ipmbSeqGet(ipmbFrame->Header.Req.rqSeqLUN); 413 uint8_t lun = 414 ipmbLunFromSeqLunGet(ipmbFrame->Header.Req.rqSeqLUN); 415 416 // prepare generic response 417 auto ipmbResponse = IpmbResponse( 418 rqSA, ipmbRespNetFn(netFn), lun, ipmbBmcTargetAddress, seq, 419 ipmbRsLun, cmd, ipmbIpmiInvalidCmd, {}); 420 421 auto buffer = ipmbResponse.ipmbToi2cConstruct(); 422 if (buffer) 423 { 424 ipmbSendI2cFrame(buffer); 425 } 426 427 goto end; 428 } 429 } 430 431 auto ipmbMessageReceived = IpmbRequest(); 432 ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r); 433 434 int devId = getDevIndex(); 435 436 std::map<std::string, std::variant<int>> options{ 437 {"rqSA", ipmbAddressTo7BitSet(ipmbMessageReceived.rqSA)}, 438 {"hostId", devId}}; 439 440 using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, 441 std::vector<uint8_t>>; 442 conn->async_method_call( 443 [this, rqLun{ipmbMessageReceived.rqLun}, 444 seq{ipmbMessageReceived.seq}, address{ipmbMessageReceived.rqSA}]( 445 const boost::system::error_code& ec, 446 const IpmiDbusRspType& response) { 447 const auto& [netfn, lun, cmd, cc, payload] = response; 448 if (ec) 449 { 450 phosphor::logging::log<phosphor::logging::level::ERR>( 451 "processI2cEvent: error getting response from IPMI"); 452 return; 453 } 454 455 uint8_t bmcTargetAddress = getBmcTargetAddress(); 456 457 if (payload.size() > ipmbMaxDataSize) 458 { 459 phosphor::logging::log<phosphor::logging::level::ERR>( 460 "processI2cEvent: response exceeding maximum size"); 461 462 // prepare generic response 463 auto ipmbResponse = IpmbResponse( 464 address, netfn, rqLun, bmcTargetAddress, seq, ipmbRsLun, 465 cmd, ipmbIpmiCmdRespNotProvided, {}); 466 467 auto buffer = ipmbResponse.ipmbToi2cConstruct(); 468 if (buffer) 469 { 470 ipmbSendI2cFrame(buffer); 471 } 472 473 return; 474 } 475 476 if (!(netfn & ipmbNetFnResponseMask)) 477 { 478 // we are not expecting request here 479 phosphor::logging::log<phosphor::logging::level::ERR>( 480 "processI2cEvent: got a request instead of response"); 481 return; 482 } 483 484 // if command is not supported, add it to filter 485 if (cc == ipmbIpmiInvalidCmd) 486 { 487 addFilter(ipmbReqNetFnFromRespNetFn(netfn), cmd); 488 } 489 490 // payload is empty after constructor invocation 491 auto ipmbResponse = 492 IpmbResponse(address, netfn, rqLun, bmcTargetAddress, seq, 493 lun, cmd, cc, payload); 494 495 auto buffer = ipmbResponse.ipmbToi2cConstruct(); 496 if (!buffer) 497 { 498 phosphor::logging::log<phosphor::logging::level::ERR>( 499 "processI2cEvent: error constructing a request"); 500 return; 501 } 502 503 ipmbSendI2cFrame(buffer); 504 }, 505 "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi", 506 "xyz.openbmc_project.Ipmi.Server", "execute", 507 ipmbMessageReceived.netFn, ipmbMessageReceived.rsLun, 508 ipmbMessageReceived.cmd, ipmbMessageReceived.data, options); 509 } 510 511 end: 512 i2cTargetDescriptor.async_wait( 513 boost::asio::posix::descriptor_base::wait_read, 514 [this](const boost::system::error_code& ec) { 515 if (ec) 516 { 517 phosphor::logging::log<phosphor::logging::level::ERR>( 518 "Error: processI2cEvent()"); 519 return; 520 } 521 522 processI2cEvent(); 523 }); 524 } 525 526 IpmbChannel::IpmbChannel(boost::asio::io_context& io, 527 uint8_t ipmbBmcTargetAddress, 528 uint8_t ipmbRqTargetAddress, uint8_t channelIdx, 529 std::shared_ptr<IpmbCommandFilter> commandFilter) : 530 i2cTargetDescriptor(io), ipmbBmcTargetAddress(ipmbBmcTargetAddress), 531 ipmbRqTargetAddress(ipmbRqTargetAddress), channelIdx(channelIdx), 532 commandFilter(commandFilter) 533 {} 534 535 int IpmbChannel::ipmbChannelInit(const char* ipmbI2cTarget) 536 { 537 // extract bus id from target path and save 538 std::string ipmbI2cTargetStr(ipmbI2cTarget); 539 auto findHyphen = ipmbI2cTargetStr.find("-"); 540 std::string busStr = ipmbI2cTargetStr.substr(findHyphen + 1); 541 try 542 { 543 ipmbBusId = std::stoi(busStr); 544 } 545 catch (const std::invalid_argument&) 546 { 547 phosphor::logging::log<phosphor::logging::level::ERR>( 548 "ipmbChannelInit: invalid bus id in target-path config"); 549 return -1; 550 } 551 552 // Check if sysfs has device. If not, enable I2C target driver by command 553 // echo "ipmb-dev 0x1010" > /sys/bus/i2c/devices/i2c-0/new_device 554 bool hasSysfs = std::filesystem::exists(ipmbI2cTarget); 555 if (!hasSysfs) 556 { 557 std::string deviceFileName = 558 "/sys/bus/i2c/devices/i2c-" + busStr + "/new_device"; 559 std::string para = "ipmb-dev 0x1010"; // init with BMC addr 0x20 560 std::fstream deviceFile; 561 deviceFile.open(deviceFileName, std::ios::out); 562 if (!deviceFile.good()) 563 { 564 phosphor::logging::log<phosphor::logging::level::ERR>( 565 "ipmbChannelInit: error opening deviceFile"); 566 return -1; 567 } 568 deviceFile << para; 569 deviceFile.close(); 570 } 571 572 // open fd to i2c target device for read write 573 ipmbi2cTargetFd = open(ipmbI2cTarget, O_RDWR | O_NONBLOCK | O_CLOEXEC); 574 if (ipmbi2cTargetFd < 0) 575 { 576 phosphor::logging::log<phosphor::logging::level::ERR>( 577 "ipmbChannelInit: error opening ipmbI2cTarget"); 578 return -1; 579 } 580 581 i2cTargetDescriptor.assign(ipmbi2cTargetFd); 582 583 i2cTargetDescriptor.async_wait( 584 boost::asio::posix::descriptor_base::wait_read, 585 [this](const boost::system::error_code& ec) { 586 if (ec) 587 { 588 phosphor::logging::log<phosphor::logging::level::ERR>( 589 "Error: processI2cEvent()"); 590 return; 591 } 592 593 processI2cEvent(); 594 }); 595 596 return 0; 597 } 598 599 int IpmbChannel::ipmbChannelUpdateTargetAddress(const uint8_t newBmcTargetAddr) 600 { 601 if (ipmbi2cTargetFd > 0) 602 { 603 i2cTargetDescriptor.close(); 604 close(ipmbi2cTargetFd); 605 ipmbi2cTargetFd = 0; 606 } 607 608 // disable old I2C target driver by command: 609 // echo "0x1010" > /sys/bus/i2c/devices/i2c-0/delete_device 610 std::string deviceFileName; 611 std::string para; 612 std::fstream deviceFile; 613 deviceFileName = "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) + 614 "/delete_device"; 615 para = "0x1010"; // align with removed ipmb0 definition in dts file 616 deviceFile.open(deviceFileName, std::ios::out); 617 if (!deviceFile.good()) 618 { 619 phosphor::logging::log<phosphor::logging::level::ERR>( 620 "ipmbChannelUpdateTargetAddress: error opening deviceFile to delete " 621 "sysfs"); 622 return -1; 623 } 624 deviceFile << para; 625 deviceFile.close(); 626 627 // enable new I2C target driver by command: 628 // echo "ipmb-dev 0x1012" > /sys/bus/i2c/devices/i2c-0/new_device 629 deviceFileName = 630 "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) + "/new_device"; 631 std::ostringstream hex; 632 uint16_t addr = 0x1000 + (newBmcTargetAddr >> 1); 633 hex << std::hex << static_cast<uint16_t>(addr); 634 const std::string& addressHexStr = hex.str(); 635 para = "ipmb-dev 0x" + addressHexStr; 636 deviceFile.open(deviceFileName, std::ios::out); 637 if (!deviceFile.good()) 638 { 639 phosphor::logging::log<phosphor::logging::level::ERR>( 640 "ipmbChannelUpdateTargetAddress: error opening deviceFile to create " 641 "sysfs"); 642 return -1; 643 } 644 deviceFile << para; 645 deviceFile.close(); 646 647 // open fd to i2c target device 648 std::string ipmbI2cTargetStr = "/dev/ipmb-" + std::to_string(ipmbBusId); 649 ipmbi2cTargetFd = open(ipmbI2cTargetStr.c_str(), O_RDWR | O_NONBLOCK); 650 if (ipmbi2cTargetFd < 0) 651 { 652 phosphor::logging::log<phosphor::logging::level::ERR>( 653 "ipmbChannelInit: error opening ipmbI2cTarget"); 654 return -1; 655 } 656 657 // start to receive i2c data as target 658 i2cTargetDescriptor.assign(ipmbi2cTargetFd); 659 i2cTargetDescriptor.async_wait( 660 boost::asio::posix::descriptor_base::wait_read, 661 [this](const boost::system::error_code& ec) { 662 if (ec) 663 { 664 phosphor::logging::log<phosphor::logging::level::ERR>( 665 "Error: processI2cEvent()"); 666 return; 667 } 668 669 processI2cEvent(); 670 }); 671 672 ipmbBmcTargetAddress = newBmcTargetAddr; 673 674 return 0; 675 } 676 677 uint8_t IpmbChannel::getBusId() 678 { 679 return ipmbBusId; 680 } 681 682 uint8_t IpmbChannel::getBmcTargetAddress() 683 { 684 return ipmbBmcTargetAddress; 685 } 686 687 uint8_t IpmbChannel::getRqTargetAddress() 688 { 689 return ipmbRqTargetAddress; 690 } 691 692 uint8_t IpmbChannel::getDevIndex() 693 { 694 return channelIdx >> 2; 695 } 696 697 uint8_t IpmbChannel::getChannelIdx() 698 { 699 return channelIdx; 700 } 701 702 ipmbChannelType IpmbChannel::getChannelType() 703 { 704 return static_cast<ipmbChannelType>((channelIdx & 3)); 705 } 706 707 void IpmbChannel::addFilter(const uint8_t respNetFn, const uint8_t cmd) 708 { 709 if (commandFilter) 710 { 711 commandFilter->addFilter(respNetFn, cmd); 712 } 713 } 714 715 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> 716 IpmbChannel::requestAdd(boost::asio::yield_context& yield, 717 std::shared_ptr<IpmbRequest> request) 718 { 719 makeRequestValid(request); 720 721 std::vector<uint8_t> buffer{}; 722 if (request->ipmbToi2cConstruct(buffer) != 0) 723 { 724 return returnStatus(ipmbResponseStatus::error); 725 } 726 727 for (int i = 0; i < ipmbNumberOfTries; i++) 728 { 729 boost::system::error_code ec; 730 int i2cRetryCnt = 0; 731 732 for (; i2cRetryCnt < ipmbI2cNumberOfRetries; i2cRetryCnt++) 733 { 734 boost::asio::async_write(i2cTargetDescriptor, 735 boost::asio::buffer(buffer), yield[ec]); 736 737 if (ec) 738 { 739 continue; // retry 740 } 741 break; 742 } 743 744 if (i2cRetryCnt == ipmbI2cNumberOfRetries) 745 { 746 std::string msgToLog = 747 "requestAdd: Sent to I2C failed after retries." 748 " busId=" + 749 std::to_string(ipmbBusId) + ", error=" + ec.message(); 750 phosphor::logging::log<phosphor::logging::level::INFO>( 751 msgToLog.c_str()); 752 } 753 754 request->timer->expires_after( 755 std::chrono::milliseconds(ipmbRequestRetryTimeout)); 756 request->timer->async_wait(yield[ec]); 757 758 if (ec && ec != boost::asio::error::operation_aborted) 759 { 760 // unexpected error - invalidate request and return generic error 761 phosphor::logging::log<phosphor::logging::level::ERR>( 762 "requestAdd: async_wait error"); 763 makeRequestInvalid(*request); 764 return returnStatus(ipmbResponseStatus::error); 765 } 766 767 if (request->state == ipmbRequestState::matched) 768 { 769 // matched response, send it to client application 770 makeRequestInvalid(*request); 771 return request->returnMatchedResponse(); 772 } 773 } 774 775 makeRequestInvalid(*request); 776 return returnStatus(ipmbResponseStatus::timeout); 777 } 778 779 static IpmbChannel* getChannel(uint8_t reqChannel) 780 { 781 auto channel = 782 std::find_if(ipmbChannels.begin(), ipmbChannels.end(), 783 [reqChannel](IpmbChannel& channel) { 784 return channel.getChannelIdx() == reqChannel; 785 }); 786 if (channel != ipmbChannels.end()) 787 { 788 return &(*channel); 789 } 790 791 return nullptr; 792 } 793 794 static int initializeChannels() 795 { 796 std::shared_ptr<IpmbCommandFilter> commandFilter = 797 std::make_shared<IpmbCommandFilter>(); 798 799 constexpr const char* configFilePath = 800 "/usr/share/ipmbbridge/ipmb-channels.json"; 801 std::ifstream configFile(configFilePath); 802 if (!configFile.is_open()) 803 { 804 phosphor::logging::log<phosphor::logging::level::ERR>( 805 "initializeChannels: Cannot open config path"); 806 return -1; 807 } 808 try 809 { 810 uint8_t devIndex = 0; 811 auto data = nlohmann::json::parse(configFile, nullptr); 812 for (const auto& channelConfig : data["channels"]) 813 { 814 const std::string& typeConfig = channelConfig["type"]; 815 const std::string& targetPath = channelConfig["slave-path"]; 816 uint8_t bmcAddr = channelConfig["bmc-addr"]; 817 uint8_t reqAddr = channelConfig["remote-addr"]; 818 819 ipmbChannelType type = ipmbChannelTypeMap.at(typeConfig); 820 821 if (channelConfig.contains("devIndex")) 822 { 823 devIndex = channelConfig["devIndex"]; 824 } 825 826 auto channel = ipmbChannels.emplace( 827 ipmbChannels.end(), io, bmcAddr, reqAddr, 828 ((devIndex << 2) | static_cast<uint8_t>(type)), commandFilter); 829 if (channel->ipmbChannelInit(targetPath.c_str()) < 0) 830 { 831 phosphor::logging::log<phosphor::logging::level::ERR>( 832 "initializeChannels: channel initialization failed"); 833 return -1; 834 } 835 } 836 } 837 catch (const nlohmann::json::exception& e) 838 { 839 phosphor::logging::log<phosphor::logging::level::ERR>( 840 "initializeChannels: Error parsing config file"); 841 return -1; 842 } 843 catch (const std::out_of_range& e) 844 { 845 phosphor::logging::log<phosphor::logging::level::ERR>( 846 "initializeChannels: Error invalid type"); 847 return -1; 848 } 849 return 0; 850 } 851 852 auto ipmbHandleRequest = 853 [](boost::asio::yield_context yield, uint8_t reqChannel, uint8_t netfn, 854 uint8_t lun, uint8_t cmd, std::vector<uint8_t> dataReceived) { 855 IpmbChannel* channel = getChannel(reqChannel); 856 857 if (channel == nullptr) 858 { 859 phosphor::logging::log<phosphor::logging::level::ERR>( 860 "ipmbHandleRequest: requested channel does not exist"); 861 return returnStatus(ipmbResponseStatus::invalid_param); 862 } 863 864 // check outstanding request list for valid sequence number 865 uint8_t seqNum = 0; 866 bool seqValid = channel->seqNumGet(seqNum); 867 if (!seqValid) 868 { 869 phosphor::logging::log<phosphor::logging::level::WARNING>( 870 "ipmbHandleRequest: cannot add more requests to the list"); 871 return returnStatus(ipmbResponseStatus::busy); 872 } 873 874 uint8_t bmcTargetAddress = channel->getBmcTargetAddress(); 875 uint8_t rqTargetAddress = channel->getRqTargetAddress(); 876 877 // construct the request to add it to outstanding request list 878 std::shared_ptr<IpmbRequest> request = std::make_shared<IpmbRequest>( 879 rqTargetAddress, netfn, ipmbRsLun, bmcTargetAddress, seqNum, lun, 880 cmd, dataReceived); 881 882 if (!request->timer) 883 { 884 phosphor::logging::log<phosphor::logging::level::ERR>( 885 "ipmbHandleRequest: timer object does not exist"); 886 return returnStatus(ipmbResponseStatus::error); 887 } 888 889 return channel->requestAdd(yield, request); 890 }; 891 892 void addUpdateTargetAddrHandler() 893 { 894 // callback to handle dbus signal of updating target addr 895 std::function<void(sdbusplus::message_t&)> updateTargetAddrHandler = 896 [](sdbusplus::message_t& message) { 897 uint8_t reqChannel, busId, targetAddr; 898 899 // valid source of signal, check whether from multi-node manager 900 std::string pathName = message.get_path(); 901 if (pathName != "/xyz/openbmc_project/MultiNode/Status") 902 { 903 phosphor::logging::log<phosphor::logging::level::ERR>( 904 "addUpdateTargetAddrHandler: invalid obj path"); 905 return; 906 } 907 908 message.read(reqChannel, busId, targetAddr); 909 910 IpmbChannel* channel = getChannel(reqChannel); 911 912 if (channel == nullptr || 913 channel->getChannelType() != ipmbChannelType::ipmb) 914 { 915 phosphor::logging::log<phosphor::logging::level::ERR>( 916 "addUpdateTargetAddrHandler: invalid channel"); 917 return; 918 } 919 if (busId != channel->getBusId()) 920 { 921 phosphor::logging::log<phosphor::logging::level::ERR>( 922 "addUpdateTargetAddrHandler: invalid busId"); 923 return; 924 } 925 if (channel->getBmcTargetAddress() == targetAddr) 926 { 927 phosphor::logging::log<phosphor::logging::level::INFO>( 928 "addUpdateTargetAddrHandler: channel bmc target addr is " 929 "unchanged, do nothing"); 930 return; 931 } 932 933 channel->ipmbChannelUpdateTargetAddress(targetAddr); 934 }; 935 936 static auto match = std::make_unique<sdbusplus::bus::match_t>( 937 static_cast<sdbusplus::bus_t&>(*conn), 938 "type='signal',member='updateBmcSlaveAddr',", updateTargetAddrHandler); 939 } 940 941 void addSendBroadcastHandler() 942 { 943 // callback to handle dbus signal of sending broadcast message 944 std::function<void(sdbusplus::message_t&)> sendBroadcastHandler = 945 [](sdbusplus::message_t& message) { 946 uint8_t reqChannel, netFn, lun, cmd; 947 std::vector<uint8_t> dataReceived; 948 message.read(reqChannel, netFn, lun, cmd, dataReceived); 949 950 IpmbChannel* channel = getChannel(reqChannel); 951 952 if (channel == nullptr) 953 { 954 phosphor::logging::log<phosphor::logging::level::ERR>( 955 "addSendBroadcastMsgHandler: requested channel does not " 956 "exist"); 957 return; 958 } 959 960 uint8_t bmcTargetAddress = channel->getBmcTargetAddress(); 961 uint8_t seqNum = 0; // seqNum is not used in broadcast msg 962 uint8_t targetAddr = broadcastAddress; 963 964 std::shared_ptr<IpmbRequest> request = 965 std::make_shared<IpmbRequest>(targetAddr, netFn, ipmbRsLun, 966 bmcTargetAddress, seqNum, lun, 967 cmd, dataReceived); 968 969 std::shared_ptr<std::vector<uint8_t>> buffer = 970 std::make_shared<std::vector<uint8_t>>(); 971 972 if (request->ipmbToi2cConstruct(*buffer) != 0) 973 { 974 return; 975 } 976 977 channel->ipmbSendI2cFrame(buffer); 978 }; 979 980 static auto match = std::make_unique<sdbusplus::bus::match_t>( 981 static_cast<sdbusplus::bus_t&>(*conn), 982 "type='signal',member='sendBroadcast',", sendBroadcastHandler); 983 } 984 985 /** 986 * @brief Main 987 */ 988 int main() 989 { 990 conn->request_name(ipmbBus); 991 992 auto server = sdbusplus::asio::object_server(conn); 993 994 std::shared_ptr<sdbusplus::asio::dbus_interface> ipmbIface = 995 server.add_interface(ipmbObj, ipmbDbusIntf); 996 997 ipmbIface->register_method("sendRequest", std::move(ipmbHandleRequest)); 998 ipmbIface->initialize(); 999 1000 if (initializeChannels() < 0) 1001 { 1002 phosphor::logging::log<phosphor::logging::level::ERR>( 1003 "Error initializeChannels"); 1004 return -1; 1005 } 1006 1007 addUpdateTargetAddrHandler(); 1008 1009 addSendBroadcastHandler(); 1010 1011 io.run(); 1012 return 0; 1013 } 1014