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 void Bridging::clearResponseQueue() 42 { 43 responseQueue.clear(); 44 } 45 46 /** 47 * @brief utils for checksum 48 */ 49 static bool ipmbChecksumValidate(uint8_t *data, uint8_t length) 50 { 51 if (data == nullptr) 52 { 53 return false; 54 } 55 56 uint8_t checksum = 0; 57 58 for (uint8_t idx = 0; idx < length; idx++) 59 { 60 checksum += data[idx]; 61 } 62 63 if (0 == checksum) 64 { 65 return true; 66 } 67 68 return false; 69 } 70 71 static uint8_t ipmbChecksumCompute(uint8_t *data, uint8_t length) 72 { 73 if (data == nullptr) 74 { 75 return 0; 76 } 77 78 uint8_t checksum = 0; 79 80 for (uint8_t idx = 0; idx < length; idx++) 81 { 82 checksum += data[idx]; 83 } 84 85 checksum = (~checksum) + 1; 86 return checksum; 87 } 88 89 static inline bool ipmbConnectionHeaderChecksumValidate(ipmbHeader *ipmbHeader) 90 { 91 return ipmbChecksumValidate(reinterpret_cast<uint8_t *>(ipmbHeader), 92 ipmbConnectionHeaderLength); 93 } 94 95 static inline bool ipmbDataChecksumValidate(ipmbHeader *ipmbHeader, 96 uint8_t length) 97 { 98 return ipmbChecksumValidate( 99 (reinterpret_cast<uint8_t *>(ipmbHeader) + ipmbConnectionHeaderLength), 100 (length - ipmbConnectionHeaderLength)); 101 } 102 103 static bool isFrameValid(ipmbHeader *frame, uint8_t length) 104 { 105 if ((length < ipmbMinFrameLength) || (length > ipmbMaxFrameLength)) 106 { 107 return false; 108 } 109 110 if (false == ipmbConnectionHeaderChecksumValidate(frame)) 111 { 112 return false; 113 } 114 115 if (false == ipmbDataChecksumValidate(frame, length)) 116 { 117 return false; 118 } 119 120 return true; 121 } 122 123 IpmbRequest::IpmbRequest(const ipmbHeader *ipmbBuffer, size_t bufferLength) 124 { 125 address = ipmbBuffer->Header.Req.address; 126 netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN); 127 rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN); 128 rqSA = ipmbBuffer->Header.Req.rqSA; 129 seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN); 130 rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN); 131 cmd = ipmbBuffer->Header.Req.cmd; 132 133 size_t dataLength = 134 bufferLength - (ipmbConnectionHeaderLength + 135 ipmbRequestDataHeaderLength + ipmbChecksumSize); 136 137 if (dataLength > 0) 138 { 139 data.insert(data.end(), ipmbBuffer->Header.Req.data, 140 &ipmbBuffer->Header.Req.data[dataLength]); 141 } 142 } 143 144 IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun, 145 uint8_t rsSA, uint8_t seq, uint8_t rsLun, 146 uint8_t cmd, uint8_t completionCode, 147 std::vector<uint8_t> &inputData) : 148 address(address), 149 netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd), 150 completionCode(completionCode) 151 { 152 data.reserve(ipmbMaxDataSize); 153 154 if (inputData.size() > 0) 155 { 156 data = std::move(inputData); 157 } 158 } 159 160 void IpmbResponse::ipmbToi2cConstruct(uint8_t *buffer, size_t *bufferLength) 161 { 162 ipmbHeader *ipmbBuffer = (ipmbHeader *)buffer; 163 164 ipmbBuffer->Header.Resp.address = address; 165 ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun); 166 ipmbBuffer->Header.Resp.rsSA = rsSA; 167 ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun); 168 ipmbBuffer->Header.Resp.cmd = cmd; 169 ipmbBuffer->Header.Resp.completionCode = completionCode; 170 171 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute( 172 buffer, ipmbConnectionHeaderLength - ipmbChecksumSize); 173 174 if (data.size() > 0) 175 { 176 std::copy( 177 data.begin(), data.end(), 178 &buffer[ipmbConnectionHeaderLength + ipmbResponseDataHeaderLength]); 179 } 180 181 *bufferLength = data.size() + ipmbResponseDataHeaderLength + 182 ipmbConnectionHeaderLength + ipmbChecksumSize; 183 184 buffer[*bufferLength - ipmbChecksumSize] = 185 ipmbChecksumCompute(&buffer[ipmbChecksum2StartOffset], 186 (ipmbResponseDataHeaderLength + data.size())); 187 } 188 189 void IpmbRequest::prepareRequest(sdbusplus::message::message &mesg) 190 { 191 mesg.append(ipmbMeChannelNum, netFn, rqLun, cmd, data); 192 } 193 194 static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd) 195 { 196 return (netFn << 8) | cmd; 197 } 198 199 static constexpr bool isMeCmdAllowed(uint8_t netFn, uint8_t cmd) 200 { 201 constexpr uint8_t netFnMeOEM = 0x2E; 202 constexpr uint8_t cmdMeOemSendRawPeci = 0x40; 203 constexpr uint8_t cmdMeOemAggSendRawPeci = 0x41; 204 constexpr uint8_t cmdMeOemCpuPkgConfWrite = 0x43; 205 constexpr uint8_t cmdMeOemCpuPciConfWrite = 0x45; 206 constexpr uint8_t cmdMeOemReadMemSmbus = 0x47; 207 constexpr uint8_t cmdMeOemWriteMemSmbus = 0x48; 208 constexpr uint8_t cmdMeOemSlotIpmb = 0x51; 209 constexpr uint8_t cmdMeOemSlotI2cMasterWriteRead = 0x52; 210 constexpr uint8_t cmdMeOemSendRawPmbus = 0xD9; 211 constexpr uint8_t cmdMeOemUnlockMeRegion = 0xE7; 212 constexpr uint8_t cmdMeOemAggSendRawPmbus = 0xEC; 213 214 switch (makeCmdKey(netFn, cmd)) 215 { 216 // Restrict ME Master write command 217 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead): 218 // Restrict ME OEM commands 219 case makeCmdKey(netFnMeOEM, cmdMeOemSendRawPeci): 220 case makeCmdKey(netFnMeOEM, cmdMeOemAggSendRawPeci): 221 case makeCmdKey(netFnMeOEM, cmdMeOemCpuPkgConfWrite): 222 case makeCmdKey(netFnMeOEM, cmdMeOemCpuPciConfWrite): 223 case makeCmdKey(netFnMeOEM, cmdMeOemReadMemSmbus): 224 case makeCmdKey(netFnMeOEM, cmdMeOemWriteMemSmbus): 225 case makeCmdKey(netFnMeOEM, cmdMeOemSlotIpmb): 226 case makeCmdKey(netFnMeOEM, cmdMeOemSlotI2cMasterWriteRead): 227 case makeCmdKey(netFnMeOEM, cmdMeOemSendRawPmbus): 228 case makeCmdKey(netFnMeOEM, cmdMeOemUnlockMeRegion): 229 case makeCmdKey(netFnMeOEM, cmdMeOemAggSendRawPmbus): 230 return false; 231 default: 232 return true; 233 } 234 } 235 236 ipmi_return_codes Bridging::handleIpmbChannel(sSendMessageReq *sendMsgReq, 237 ipmi_response_t response, 238 ipmi_data_len_t dataLen) 239 { 240 if ((*dataLen < (sizeof(sSendMessageReq) + ipmbMinFrameLength)) || 241 (*dataLen > (sizeof(sSendMessageReq) + ipmbMaxFrameLength))) 242 { 243 *dataLen = 0; 244 return IPMI_CC_REQ_DATA_LEN_INVALID; 245 } 246 247 auto sendMsgReqData = reinterpret_cast<ipmbHeader *>(sendMsgReq->data); 248 249 // TODO: check privilege lvl. Bridging to ME requires Administrator lvl 250 251 // allow bridging to ME only 252 if (sendMsgReqData->Header.Req.address != ipmbMeSlaveAddress) 253 { 254 phosphor::logging::log<phosphor::logging::level::INFO>( 255 "handleIpmbChannel, IPMB address invalid"); 256 *dataLen = 0; 257 return IPMI_CC_PARM_OUT_OF_RANGE; 258 } 259 260 constexpr uint8_t shiftLUN = 2; 261 if (!isMeCmdAllowed((sendMsgReqData->Header.Req.rsNetFnLUN >> shiftLUN), 262 sendMsgReqData->Header.Req.cmd)) 263 { 264 return IPMI_CC_INVALID_FIELD_REQUEST; 265 } 266 267 // check allowed modes 268 if (sendMsgReq->modeGet() != modeNoTracking && 269 sendMsgReq->modeGet() != modeTrackRequest) 270 { 271 phosphor::logging::log<phosphor::logging::level::INFO>( 272 "handleIpmbChannel, mode not supported"); 273 *dataLen = 0; 274 return IPMI_CC_PARM_OUT_OF_RANGE; 275 } 276 277 // check if request contains valid IPMB frame 278 if (!isFrameValid(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq)))) 279 { 280 phosphor::logging::log<phosphor::logging::level::INFO>( 281 "handleIpmbChannel, IPMB frame invalid"); 282 *dataLen = 0; 283 return IPMI_CC_PARM_OUT_OF_RANGE; 284 } 285 286 auto ipmbRequest = 287 IpmbRequest(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq))); 288 289 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> 290 ipmbResponse; 291 292 // send request to IPMB 293 try 294 { 295 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 296 auto mesg = 297 dbus->new_method_call(ipmbBus, ipmbObj, ipmbIntf, "sendRequest"); 298 ipmbRequest.prepareRequest(mesg); 299 auto ret = dbus->call(mesg); 300 ret.read(ipmbResponse); 301 } 302 catch (sdbusplus::exception::SdBusError &e) 303 { 304 phosphor::logging::log<phosphor::logging::level::ERR>( 305 "handleIpmbChannel, dbus call exception"); 306 *dataLen = 0; 307 return IPMI_CC_UNSPECIFIED_ERROR; 308 } 309 310 std::vector<uint8_t> dataReceived(0); 311 int status = -1; 312 uint8_t netFn = 0, lun = 0, cmd = 0, cc = 0; 313 314 std::tie(status, netFn, lun, cmd, cc, dataReceived) = ipmbResponse; 315 316 auto respReceived = 317 IpmbResponse(ipmbRequest.rqSA, netFn, lun, ipmbRequest.address, 318 ipmbRequest.seq, lun, cmd, cc, dataReceived); 319 320 // check IPMB layer status 321 if (status) 322 { 323 phosphor::logging::log<phosphor::logging::level::WARNING>( 324 "handleIpmbChannel, ipmb returned non zero status"); 325 *dataLen = 0; 326 return IPMI_CC_RESPONSE_ERROR; 327 } 328 329 auto sendMsgRes = reinterpret_cast<uint8_t *>(response); 330 331 switch (sendMsgReq->modeGet()) 332 { 333 case modeNoTracking: 334 if (responseQueue.size() == responseQueueMaxSize) 335 { 336 *dataLen = 0; 337 return IPMI_CC_BUSY; 338 } 339 responseQueue.insert(responseQueue.end(), std::move(respReceived)); 340 *dataLen = 0; 341 return IPMI_CC_OK; 342 343 break; 344 case modeTrackRequest: 345 respReceived.ipmbToi2cConstruct(sendMsgRes, dataLen); 346 return IPMI_CC_OK; 347 348 break; 349 default: 350 phosphor::logging::log<phosphor::logging::level::INFO>( 351 "handleIpmbChannel, mode not supported"); 352 *dataLen = 0; 353 return IPMI_CC_PARM_OUT_OF_RANGE; 354 } 355 356 *dataLen = 0; 357 return IPMI_CC_UNSPECIFIED_ERROR; 358 } 359 360 ipmi_return_codes Bridging::sendMessageHandler(ipmi_request_t request, 361 ipmi_response_t response, 362 ipmi_data_len_t dataLen) 363 { 364 ipmi_return_codes retCode = IPMI_CC_OK; 365 366 if (*dataLen < sizeof(sSendMessageReq)) 367 { 368 *dataLen = 0; 369 return IPMI_CC_REQ_DATA_LEN_INVALID; 370 } 371 372 auto sendMsgReq = reinterpret_cast<sSendMessageReq *>(request); 373 374 // check message fields: 375 // encryption not supported 376 if (sendMsgReq->encryptionGet() != 0) 377 { 378 phosphor::logging::log<phosphor::logging::level::INFO>( 379 "sendMessageHandler, encryption not supported"); 380 *dataLen = 0; 381 return IPMI_CC_PARM_OUT_OF_RANGE; 382 } 383 384 // authentication not supported 385 if (sendMsgReq->authenticationGet() != 0) 386 { 387 phosphor::logging::log<phosphor::logging::level::INFO>( 388 "sendMessageHandler, authentication not supported"); 389 *dataLen = 0; 390 return IPMI_CC_PARM_OUT_OF_RANGE; 391 } 392 393 switch (sendMsgReq->channelNumGet()) 394 { 395 // we only handle ipmb for now 396 case targetChannelIpmb: 397 case targetChannelOtherLan: 398 retCode = handleIpmbChannel(sendMsgReq, response, dataLen); 399 break; 400 // fall through to default 401 case targetChannelIcmb10: 402 case targetChannelIcmb09: 403 case targetChannelLan: 404 case targetChannelSerialModem: 405 case targetChannelPciSmbus: 406 case targetChannelSmbus10: 407 case targetChannelSmbus20: 408 case targetChannelSystemInterface: 409 default: 410 phosphor::logging::log<phosphor::logging::level::INFO>( 411 "sendMessageHandler, TargetChannel invalid"); 412 *dataLen = 0; 413 return IPMI_CC_PARM_OUT_OF_RANGE; 414 } 415 416 return retCode; 417 } 418 419 ipmi_return_codes Bridging::getMessageHandler(ipmi_request_t request, 420 ipmi_response_t response, 421 ipmi_data_len_t dataLen) 422 { 423 if (*dataLen != 0) 424 { 425 *dataLen = 0; 426 return IPMI_CC_REQ_DATA_LEN_INVALID; 427 } 428 429 auto getMsgRes = reinterpret_cast<sGetMessageRes *>(response); 430 auto getMsgResData = static_cast<uint8_t *>(getMsgRes->data); 431 432 std::memset(getMsgRes, 0, sizeof(sGetMessageRes)); 433 434 auto respQueueItem = responseQueue.begin(); 435 436 if (respQueueItem == responseQueue.end()) 437 { 438 phosphor::logging::log<phosphor::logging::level::INFO>( 439 "getMessageHandler, no data available"); 440 *dataLen = 0; 441 return ipmiGetMessageCmdDataNotAvailable; 442 } 443 444 // set message fields 445 getMsgRes->privilegeLvlSet(SYSTEM_INTERFACE); 446 getMsgRes->channelNumSet(targetChannelSystemInterface); 447 448 // construct response 449 respQueueItem->ipmbToi2cConstruct(getMsgResData, dataLen); 450 responseQueue.erase(respQueueItem); 451 452 *dataLen = *dataLen + sizeof(sGetMessageRes); 453 return IPMI_CC_OK; 454 } 455 456 ipmi_ret_t ipmiAppSendMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd, 457 ipmi_request_t request, ipmi_response_t response, 458 ipmi_data_len_t dataLen, ipmi_context_t context) 459 { 460 ipmi_ret_t retCode = IPMI_CC_OK; 461 retCode = bridging.sendMessageHandler(request, response, dataLen); 462 463 return retCode; 464 } 465 466 ipmi_ret_t ipmiAppGetMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd, 467 ipmi_request_t request, ipmi_response_t response, 468 ipmi_data_len_t dataLen, ipmi_context_t context) 469 { 470 ipmi_ret_t retCode = IPMI_CC_OK; 471 retCode = bridging.getMessageHandler(request, response, dataLen); 472 473 return retCode; 474 } 475 476 std::size_t Bridging::getResponseQueueSize() 477 { 478 return responseQueue.size(); 479 } 480 481 /** 482 @brief This command is used to retrive present message available states. 483 484 @return IPMI completion code plus Flags as response data on success. 485 **/ 486 ipmi::RspType<std::bitset<8>> ipmiAppGetMessageFlags() 487 { 488 std::bitset<8> getMsgFlagsRes; 489 490 getMsgFlagsRes.set(getMsgFlagEventMessageBit); 491 492 // set message fields 493 if (bridging.getResponseQueueSize() > 0) 494 { 495 getMsgFlagsRes.set(getMsgFlagReceiveMessageBit); 496 } 497 else 498 { 499 getMsgFlagsRes.reset(getMsgFlagReceiveMessageBit); 500 } 501 502 try 503 { 504 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 505 ipmi::Value variant = ipmi::getDbusProperty( 506 *dbus, wdtService, wdtObjPath, wdtInterface, wdtInterruptFlagProp); 507 if (std::get<bool>(variant)) 508 { 509 getMsgFlagsRes.set(getMsgFlagWatchdogPreTimeOutBit); 510 } 511 } 512 catch (sdbusplus::exception::SdBusError &e) 513 { 514 phosphor::logging::log<phosphor::logging::level::ERR>( 515 "ipmiAppGetMessageFlags, dbus call exception"); 516 return ipmi::responseUnspecifiedError(); 517 } 518 519 return ipmi::responseSuccess(getMsgFlagsRes); 520 } 521 522 /** @brief This command is used to flush unread data from the receive 523 * message queue 524 * @param receiveMessage - clear receive message queue 525 * @param eventMsgBufFull - clear event message buffer full 526 * @param reserved2 - reserved bit 527 * @param watchdogTimeout - clear watchdog pre-timeout interrupt flag 528 * @param reserved1 - reserved bit 529 * @param oem0 - clear OEM 0 data 530 * @param oem1 - clear OEM 1 data 531 * @param oem2 - clear OEM 2 data 532 533 * @return IPMI completion code on success 534 */ 535 ipmi::RspType<> ipmiAppClearMessageFlags(bool receiveMessage, 536 bool eventMsgBufFull, bool reserved2, 537 bool watchdogTimeout, bool reserved1, 538 bool oem0, bool oem1, bool oem2) 539 { 540 if (reserved1 || reserved2) 541 { 542 return ipmi::responseInvalidFieldRequest(); 543 } 544 545 if (receiveMessage) 546 { 547 bridging.clearResponseQueue(); 548 } 549 return ipmi::responseSuccess(); 550 } 551 552 static void register_bridging_functions() __attribute__((constructor)); 553 static void register_bridging_functions() 554 { 555 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp, 556 ipmi::app::cmdClearMessageFlags, 557 ipmi::Privilege::User, ipmiAppClearMessageFlags); 558 559 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp, 560 ipmi::app::cmdGetMessageFlags, ipmi::Privilege::User, 561 ipmiAppGetMessageFlags); 562 563 ipmi_register_callback(NETFUN_APP, 564 Bridging::IpmiAppBridgingCmds::ipmiCmdGetMessage, 565 NULL, ipmiAppGetMessage, PRIVILEGE_USER); 566 567 ipmi_register_callback(NETFUN_APP, 568 Bridging::IpmiAppBridgingCmds::ipmiCmdSendMessage, 569 NULL, ipmiAppSendMessage, PRIVILEGE_USER); 570 571 return; 572 } 573