1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #include <bitset> 18 #include <bridgingcommands.hpp> 19 #include <cstring> 20 #include <ipmid/api.hpp> 21 #include <ipmid/utils.hpp> 22 #include <phosphor-logging/log.hpp> 23 #include <sdbusplus/bus.hpp> 24 #include <sdbusplus/bus/match.hpp> 25 #include <sdbusplus/message.hpp> 26 #include <vector> 27 28 static constexpr const char *wdtService = "xyz.openbmc_project.Watchdog"; 29 static constexpr const char *wdtInterface = 30 "xyz.openbmc_project.State.Watchdog"; 31 static constexpr const char *wdtObjPath = "/xyz/openbmc_project/watchdog/host0"; 32 static constexpr const char *wdtInterruptFlagProp = 33 "PreTimeoutInterruptOccurFlag"; 34 35 static constexpr const char *ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb"; 36 static constexpr const char *ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb"; 37 static constexpr const char *ipmbIntf = "org.openbmc.Ipmb"; 38 39 static Bridging bridging; 40 41 /** 42 * @brief utils for checksum 43 */ 44 static bool ipmbChecksumValidate(uint8_t *data, uint8_t length) 45 { 46 if (data == nullptr) 47 { 48 return false; 49 } 50 51 uint8_t checksum = 0; 52 53 for (uint8_t idx = 0; idx < length; idx++) 54 { 55 checksum += data[idx]; 56 } 57 58 if (0 == checksum) 59 { 60 return true; 61 } 62 63 return false; 64 } 65 66 static uint8_t ipmbChecksumCompute(uint8_t *data, uint8_t length) 67 { 68 if (data == nullptr) 69 { 70 return 0; 71 } 72 73 uint8_t checksum = 0; 74 75 for (uint8_t idx = 0; idx < length; idx++) 76 { 77 checksum += data[idx]; 78 } 79 80 checksum = (~checksum) + 1; 81 return checksum; 82 } 83 84 static inline bool ipmbConnectionHeaderChecksumValidate(ipmbHeader *ipmbHeader) 85 { 86 return ipmbChecksumValidate(reinterpret_cast<uint8_t *>(ipmbHeader), 87 ipmbConnectionHeaderLength); 88 } 89 90 static inline bool ipmbDataChecksumValidate(ipmbHeader *ipmbHeader, 91 uint8_t length) 92 { 93 return ipmbChecksumValidate( 94 (reinterpret_cast<uint8_t *>(ipmbHeader) + ipmbConnectionHeaderLength), 95 (length - ipmbConnectionHeaderLength)); 96 } 97 98 static bool isFrameValid(ipmbHeader *frame, uint8_t length) 99 { 100 if ((length < ipmbMinFrameLength) || (length > ipmbMaxFrameLength)) 101 { 102 return false; 103 } 104 105 if (false == ipmbConnectionHeaderChecksumValidate(frame)) 106 { 107 return false; 108 } 109 110 if (false == ipmbDataChecksumValidate(frame, length)) 111 { 112 return false; 113 } 114 115 return true; 116 } 117 118 IpmbRequest::IpmbRequest(const ipmbHeader *ipmbBuffer, size_t bufferLength) 119 { 120 address = ipmbBuffer->Header.Req.address; 121 netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN); 122 rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN); 123 rqSA = ipmbBuffer->Header.Req.rqSA; 124 seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN); 125 rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN); 126 cmd = ipmbBuffer->Header.Req.cmd; 127 128 size_t dataLength = 129 bufferLength - (ipmbConnectionHeaderLength + 130 ipmbRequestDataHeaderLength + ipmbChecksumSize); 131 132 if (dataLength > 0) 133 { 134 data.insert(data.end(), ipmbBuffer->Header.Req.data, 135 &ipmbBuffer->Header.Req.data[dataLength]); 136 } 137 } 138 139 IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun, 140 uint8_t rsSA, uint8_t seq, uint8_t rsLun, 141 uint8_t cmd, uint8_t completionCode, 142 std::vector<uint8_t> &inputData) : 143 address(address), 144 netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd), 145 completionCode(completionCode) 146 { 147 data.reserve(ipmbMaxDataSize); 148 149 if (inputData.size() > 0) 150 { 151 data = std::move(inputData); 152 } 153 } 154 155 void IpmbResponse::ipmbToi2cConstruct(uint8_t *buffer, size_t *bufferLength) 156 { 157 ipmbHeader *ipmbBuffer = (ipmbHeader *)buffer; 158 159 ipmbBuffer->Header.Resp.address = address; 160 ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun); 161 ipmbBuffer->Header.Resp.rsSA = rsSA; 162 ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun); 163 ipmbBuffer->Header.Resp.cmd = cmd; 164 ipmbBuffer->Header.Resp.completionCode = completionCode; 165 166 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute( 167 buffer, ipmbConnectionHeaderLength - ipmbChecksumSize); 168 169 if (data.size() > 0) 170 { 171 std::copy( 172 data.begin(), data.end(), 173 &buffer[ipmbConnectionHeaderLength + ipmbResponseDataHeaderLength]); 174 } 175 176 *bufferLength = data.size() + ipmbResponseDataHeaderLength + 177 ipmbConnectionHeaderLength + ipmbChecksumSize; 178 179 buffer[*bufferLength - ipmbChecksumSize] = 180 ipmbChecksumCompute(&buffer[ipmbChecksum2StartOffset], 181 (ipmbResponseDataHeaderLength + data.size())); 182 } 183 184 void IpmbRequest::prepareRequest(sdbusplus::message::message &mesg) 185 { 186 mesg.append(ipmbMeChannelNum, netFn, rqLun, cmd, data); 187 } 188 189 ipmi_return_codes Bridging::handleIpmbChannel(sSendMessageReq *sendMsgReq, 190 ipmi_response_t response, 191 ipmi_data_len_t dataLen) 192 { 193 if ((*dataLen < (sizeof(sSendMessageReq) + ipmbMinFrameLength)) || 194 (*dataLen > (sizeof(sSendMessageReq) + ipmbMaxFrameLength))) 195 { 196 *dataLen = 0; 197 return IPMI_CC_REQ_DATA_LEN_INVALID; 198 } 199 200 auto sendMsgReqData = reinterpret_cast<ipmbHeader *>(sendMsgReq->data); 201 202 // TODO: check privilege lvl. Bridging to ME requires Administrator lvl 203 204 // allow bridging to ME only 205 if (sendMsgReqData->Header.Req.address != ipmbMeSlaveAddress) 206 { 207 phosphor::logging::log<phosphor::logging::level::INFO>( 208 "handleIpmbChannel, IPMB address invalid"); 209 *dataLen = 0; 210 return IPMI_CC_PARM_OUT_OF_RANGE; 211 } 212 213 // check allowed modes 214 if (sendMsgReq->modeGet() != modeNoTracking && 215 sendMsgReq->modeGet() != modeTrackRequest) 216 { 217 phosphor::logging::log<phosphor::logging::level::INFO>( 218 "handleIpmbChannel, mode not supported"); 219 *dataLen = 0; 220 return IPMI_CC_PARM_OUT_OF_RANGE; 221 } 222 223 // check if request contains valid IPMB frame 224 if (!isFrameValid(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq)))) 225 { 226 phosphor::logging::log<phosphor::logging::level::INFO>( 227 "handleIpmbChannel, IPMB frame invalid"); 228 *dataLen = 0; 229 return IPMI_CC_PARM_OUT_OF_RANGE; 230 } 231 232 auto ipmbRequest = 233 IpmbRequest(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq))); 234 235 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> 236 ipmbResponse; 237 238 // send request to IPMB 239 try 240 { 241 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 242 auto mesg = 243 dbus->new_method_call(ipmbBus, ipmbObj, ipmbIntf, "sendRequest"); 244 ipmbRequest.prepareRequest(mesg); 245 auto ret = dbus->call(mesg); 246 ret.read(ipmbResponse); 247 } 248 catch (sdbusplus::exception::SdBusError &e) 249 { 250 phosphor::logging::log<phosphor::logging::level::ERR>( 251 "handleIpmbChannel, dbus call exception"); 252 *dataLen = 0; 253 return IPMI_CC_UNSPECIFIED_ERROR; 254 } 255 256 std::vector<uint8_t> dataReceived(0); 257 int status = -1; 258 uint8_t netFn = 0, lun = 0, cmd = 0, cc = 0; 259 260 std::tie(status, netFn, lun, cmd, cc, dataReceived) = ipmbResponse; 261 262 auto respReceived = 263 IpmbResponse(ipmbRequest.rqSA, netFn, lun, ipmbRequest.address, 264 ipmbRequest.seq, lun, cmd, cc, dataReceived); 265 266 // check IPMB layer status 267 if (status) 268 { 269 phosphor::logging::log<phosphor::logging::level::WARNING>( 270 "handleIpmbChannel, ipmb returned non zero status"); 271 *dataLen = 0; 272 return IPMI_CC_RESPONSE_ERROR; 273 } 274 275 auto sendMsgRes = reinterpret_cast<uint8_t *>(response); 276 277 switch (sendMsgReq->modeGet()) 278 { 279 case modeNoTracking: 280 if (responseQueue.size() == responseQueueMaxSize) 281 { 282 *dataLen = 0; 283 return IPMI_CC_BUSY; 284 } 285 responseQueue.insert(responseQueue.end(), std::move(respReceived)); 286 *dataLen = 0; 287 return IPMI_CC_OK; 288 289 break; 290 case modeTrackRequest: 291 respReceived.ipmbToi2cConstruct(sendMsgRes, dataLen); 292 return IPMI_CC_OK; 293 294 break; 295 default: 296 phosphor::logging::log<phosphor::logging::level::INFO>( 297 "handleIpmbChannel, mode not supported"); 298 *dataLen = 0; 299 return IPMI_CC_PARM_OUT_OF_RANGE; 300 } 301 302 *dataLen = 0; 303 return IPMI_CC_UNSPECIFIED_ERROR; 304 } 305 306 ipmi_return_codes Bridging::sendMessageHandler(ipmi_request_t request, 307 ipmi_response_t response, 308 ipmi_data_len_t dataLen) 309 { 310 ipmi_return_codes retCode = IPMI_CC_OK; 311 312 if (*dataLen < sizeof(sSendMessageReq)) 313 { 314 *dataLen = 0; 315 return IPMI_CC_REQ_DATA_LEN_INVALID; 316 } 317 318 auto sendMsgReq = reinterpret_cast<sSendMessageReq *>(request); 319 320 // check message fields: 321 // encryption not supported 322 if (sendMsgReq->encryptionGet() != 0) 323 { 324 phosphor::logging::log<phosphor::logging::level::INFO>( 325 "sendMessageHandler, encryption not supported"); 326 *dataLen = 0; 327 return IPMI_CC_PARM_OUT_OF_RANGE; 328 } 329 330 // authentication not supported 331 if (sendMsgReq->authenticationGet() != 0) 332 { 333 phosphor::logging::log<phosphor::logging::level::INFO>( 334 "sendMessageHandler, authentication not supported"); 335 *dataLen = 0; 336 return IPMI_CC_PARM_OUT_OF_RANGE; 337 } 338 339 switch (sendMsgReq->channelNumGet()) 340 { 341 // we only handle ipmb for now 342 case targetChannelIpmb: 343 case targetChannelOtherLan: 344 retCode = handleIpmbChannel(sendMsgReq, response, dataLen); 345 break; 346 // fall through to default 347 case targetChannelIcmb10: 348 case targetChannelIcmb09: 349 case targetChannelLan: 350 case targetChannelSerialModem: 351 case targetChannelPciSmbus: 352 case targetChannelSmbus10: 353 case targetChannelSmbus20: 354 case targetChannelSystemInterface: 355 default: 356 phosphor::logging::log<phosphor::logging::level::INFO>( 357 "sendMessageHandler, TargetChannel invalid"); 358 *dataLen = 0; 359 return IPMI_CC_PARM_OUT_OF_RANGE; 360 } 361 362 return retCode; 363 } 364 365 ipmi_return_codes Bridging::getMessageHandler(ipmi_request_t request, 366 ipmi_response_t response, 367 ipmi_data_len_t dataLen) 368 { 369 if (*dataLen != 0) 370 { 371 *dataLen = 0; 372 return IPMI_CC_REQ_DATA_LEN_INVALID; 373 } 374 375 auto getMsgRes = reinterpret_cast<sGetMessageRes *>(response); 376 auto getMsgResData = static_cast<uint8_t *>(getMsgRes->data); 377 378 std::memset(getMsgRes, 0, sizeof(sGetMessageRes)); 379 380 auto respQueueItem = responseQueue.begin(); 381 382 if (respQueueItem == responseQueue.end()) 383 { 384 phosphor::logging::log<phosphor::logging::level::INFO>( 385 "getMessageHandler, no data available"); 386 *dataLen = 0; 387 return ipmiGetMessageCmdDataNotAvailable; 388 } 389 390 // set message fields 391 getMsgRes->privilegeLvlSet(SYSTEM_INTERFACE); 392 getMsgRes->channelNumSet(targetChannelSystemInterface); 393 394 // construct response 395 respQueueItem->ipmbToi2cConstruct(getMsgResData, dataLen); 396 responseQueue.erase(respQueueItem); 397 398 *dataLen = *dataLen + sizeof(sGetMessageRes); 399 return IPMI_CC_OK; 400 } 401 402 ipmi_return_codes Bridging::clearMessageFlagsHandler(ipmi_request_t request, 403 ipmi_response_t response, 404 ipmi_data_len_t dataLen) 405 { 406 if (*dataLen != sizeof(sClearMessageFlagsReq)) 407 { 408 return IPMI_CC_REQ_DATA_LEN_INVALID; 409 } 410 411 auto clearMsgFlagsReq = reinterpret_cast<sClearMessageFlagsReq *>(request); 412 413 if (clearMsgFlagsReq->receiveMessageBitGet() == 1) 414 { 415 responseQueue.clear(); 416 } 417 418 return IPMI_CC_OK; 419 } 420 421 ipmi_ret_t ipmiAppSendMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd, 422 ipmi_request_t request, ipmi_response_t response, 423 ipmi_data_len_t dataLen, ipmi_context_t context) 424 { 425 ipmi_ret_t retCode = IPMI_CC_OK; 426 retCode = bridging.sendMessageHandler(request, response, dataLen); 427 428 return retCode; 429 } 430 431 ipmi_ret_t ipmiAppGetMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd, 432 ipmi_request_t request, ipmi_response_t response, 433 ipmi_data_len_t dataLen, ipmi_context_t context) 434 { 435 ipmi_ret_t retCode = IPMI_CC_OK; 436 retCode = bridging.getMessageHandler(request, response, dataLen); 437 438 return retCode; 439 } 440 441 std::size_t Bridging::getResponseQueueSize() 442 { 443 return responseQueue.size(); 444 } 445 446 /** 447 @brief This command is used to retrive present message available states. 448 449 @return IPMI completion code plus Flags as response data on success. 450 **/ 451 ipmi::RspType<std::bitset<8>> ipmiAppGetMessageFlags() 452 { 453 std::bitset<8> getMsgFlagsRes; 454 455 getMsgFlagsRes.set(getMsgFlagEventMessageBit); 456 457 // set message fields 458 if (bridging.getResponseQueueSize() > 0) 459 { 460 getMsgFlagsRes.set(getMsgFlagReceiveMessageBit); 461 } 462 else 463 { 464 getMsgFlagsRes.reset(getMsgFlagReceiveMessageBit); 465 } 466 467 try 468 { 469 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 470 ipmi::Value variant = ipmi::getDbusProperty( 471 *dbus, wdtService, wdtObjPath, wdtInterface, wdtInterruptFlagProp); 472 if (std::get<bool>(variant)) 473 { 474 getMsgFlagsRes.set(getMsgFlagWatchdogPreTimeOutBit); 475 } 476 } 477 catch (sdbusplus::exception::SdBusError &e) 478 { 479 phosphor::logging::log<phosphor::logging::level::ERR>( 480 "ipmiAppGetMessageFlags, dbus call exception"); 481 return ipmi::responseUnspecifiedError(); 482 } 483 484 return ipmi::responseSuccess(getMsgFlagsRes); 485 } 486 487 ipmi_ret_t ipmiAppClearMessageFlags(ipmi_netfn_t netFn, ipmi_cmd_t cmd, 488 ipmi_request_t request, 489 ipmi_response_t response, 490 ipmi_data_len_t dataLen, 491 ipmi_context_t context) 492 { 493 ipmi_ret_t retCode = IPMI_CC_OK; 494 retCode = bridging.clearMessageFlagsHandler(request, response, dataLen); 495 496 *dataLen = 0; 497 498 return retCode; 499 } 500 501 static void register_bridging_functions() __attribute__((constructor)); 502 static void register_bridging_functions() 503 { 504 ipmi_register_callback( 505 NETFUN_APP, Bridging::IpmiAppBridgingCmds::ipmiCmdClearMessageFlags, 506 NULL, ipmiAppClearMessageFlags, PRIVILEGE_USER); 507 508 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp, 509 ipmi::app::cmdGetMessageFlags, ipmi::Privilege::User, 510 ipmiAppGetMessageFlags); 511 512 ipmi_register_callback(NETFUN_APP, 513 Bridging::IpmiAppBridgingCmds::ipmiCmdGetMessage, 514 NULL, ipmiAppGetMessage, PRIVILEGE_USER); 515 516 ipmi_register_callback(NETFUN_APP, 517 Bridging::IpmiAppBridgingCmds::ipmiCmdSendMessage, 518 NULL, ipmiAppSendMessage, PRIVILEGE_USER); 519 520 return; 521 } 522