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