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 <boost/container/flat_map.hpp> 18 #include <filesystem> 19 #include <fstream> 20 #include <ipmid/api.hpp> 21 #include <manufacturingcommands.hpp> 22 #include <oemcommands.hpp> 23 24 namespace ipmi 25 { 26 27 Manufacturing mtm; 28 29 static auto revertTimeOut = 30 std::chrono::duration_cast<std::chrono::microseconds>( 31 std::chrono::seconds(60)); // 1 minute timeout 32 33 static constexpr uint8_t slotAddressTypeBus = 0; 34 static constexpr uint8_t slotAddressTypeUniqueid = 1; 35 static constexpr uint8_t slotI2CMaxReadSize = 35; 36 37 static constexpr const char* callbackMgrService = 38 "xyz.openbmc_project.CallbackManager"; 39 static constexpr const char* callbackMgrIntf = 40 "xyz.openbmc_project.CallbackManager"; 41 static constexpr const char* callbackMgrObjPath = 42 "/xyz/openbmc_project/CallbackManager"; 43 static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate"; 44 45 const static constexpr char* systemDService = "org.freedesktop.systemd1"; 46 const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1"; 47 const static constexpr char* systemDMgrIntf = 48 "org.freedesktop.systemd1.Manager"; 49 const static constexpr char* pidControlService = "phosphor-pid-control.service"; 50 51 static inline Cc resetMtmTimer(boost::asio::yield_context yield) 52 { 53 auto sdbusp = getSdBus(); 54 boost::system::error_code ec; 55 sdbusp->yield_method_call<>(yield, ec, specialModeService, 56 specialModeObjPath, specialModeIntf, 57 "ResetTimer"); 58 if (ec) 59 { 60 phosphor::logging::log<phosphor::logging::level::ERR>( 61 "Failed to reset the manufacturing mode timer"); 62 return ccUnspecifiedError; 63 } 64 return ccSuccess; 65 } 66 67 int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path) 68 { 69 switch (signal) 70 { 71 case SmSignalGet::smPowerButton: 72 path = "/xyz/openbmc_project/chassis/buttons/power"; 73 break; 74 case SmSignalGet::smResetButton: 75 path = "/xyz/openbmc_project/chassis/buttons/reset"; 76 break; 77 case SmSignalGet::smNMIButton: 78 path = "/xyz/openbmc_project/chassis/buttons/nmi"; 79 break; 80 case SmSignalGet::smIdentifyButton: 81 path = "/xyz/openbmc_project/chassis/buttons/id"; 82 break; 83 default: 84 return -1; 85 break; 86 } 87 return 0; 88 } 89 90 ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState) 91 { 92 LedProperty* ledProp = mtm.findLedProperty(signal); 93 if (ledProp == nullptr) 94 { 95 return IPMI_CC_INVALID_FIELD_REQUEST; 96 } 97 98 std::string ledName = ledProp->getName(); 99 std::string ledService = ledServicePrefix + ledName; 100 std::string ledPath = ledPathPrefix + ledName; 101 ipmi::Value presentState; 102 103 if (false == ledProp->getLock()) 104 { 105 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf, 106 "State", &presentState) != 0) 107 { 108 return IPMI_CC_UNSPECIFIED_ERROR; 109 } 110 ledProp->setPrevState(std::get<std::string>(presentState)); 111 ledProp->setLock(true); 112 if (signal == SmSignalSet::smPowerFaultLed || 113 signal == SmSignalSet::smSystemReadyLed) 114 { 115 mtm.revertLedCallback = true; 116 } 117 } 118 if (mtm.setProperty(ledService, ledPath, ledIntf, "State", 119 ledStateStr + setState) != 0) 120 { 121 return IPMI_CC_UNSPECIFIED_ERROR; 122 } 123 return IPMI_CC_OK; 124 } 125 126 ipmi_ret_t ledRevert(SmSignalSet signal) 127 { 128 LedProperty* ledProp = mtm.findLedProperty(signal); 129 if (ledProp == nullptr) 130 { 131 return IPMI_CC_INVALID_FIELD_REQUEST; 132 } 133 if (true == ledProp->getLock()) 134 { 135 ledProp->setLock(false); 136 if (signal == SmSignalSet::smPowerFaultLed || 137 signal == SmSignalSet::smSystemReadyLed) 138 { 139 try 140 { 141 ipmi::method_no_args::callDbusMethod( 142 *getSdBus(), callbackMgrService, callbackMgrObjPath, 143 callbackMgrIntf, retriggerLedUpdate); 144 } 145 catch (sdbusplus::exception_t& e) 146 { 147 return IPMI_CC_UNSPECIFIED_ERROR; 148 } 149 mtm.revertLedCallback = false; 150 } 151 else 152 { 153 std::string ledName = ledProp->getName(); 154 std::string ledService = ledServicePrefix + ledName; 155 std::string ledPath = ledPathPrefix + ledName; 156 if (mtm.setProperty(ledService, ledPath, ledIntf, "State", 157 ledProp->getPrevState()) != 0) 158 { 159 return IPMI_CC_UNSPECIFIED_ERROR; 160 } 161 } 162 } 163 return IPMI_CC_OK; 164 } 165 166 void Manufacturing::initData() 167 { 168 ledPropertyList.push_back( 169 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber")); 170 ledPropertyList.push_back( 171 LedProperty(SmSignalSet::smSystemReadyLed, "status_green")); 172 ledPropertyList.push_back( 173 LedProperty(SmSignalSet::smIdentifyLed, "identify")); 174 } 175 176 void Manufacturing::revertTimerHandler() 177 { 178 if (revertFanPWM) 179 { 180 revertFanPWM = false; 181 disablePidControlService(false); 182 } 183 184 for (const auto& ledProperty : ledPropertyList) 185 { 186 const std::string& ledName = ledProperty.getName(); 187 ledRevert(ledProperty.getSignal()); 188 } 189 } 190 191 Manufacturing::Manufacturing() : 192 revertTimer([&](void) { revertTimerHandler(); }) 193 { 194 initData(); 195 } 196 197 int8_t Manufacturing::getProperty(const std::string& service, 198 const std::string& path, 199 const std::string& interface, 200 const std::string& propertyName, 201 ipmi::Value* reply) 202 { 203 try 204 { 205 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface, 206 propertyName); 207 } 208 catch (const sdbusplus::exception::SdBusError& e) 209 { 210 phosphor::logging::log<phosphor::logging::level::INFO>( 211 "ERROR: getProperty"); 212 return -1; 213 } 214 215 return 0; 216 } 217 218 int8_t Manufacturing::setProperty(const std::string& service, 219 const std::string& path, 220 const std::string& interface, 221 const std::string& propertyName, 222 ipmi::Value value) 223 { 224 try 225 { 226 ipmi::setDbusProperty(*getSdBus(), service, path, interface, 227 propertyName, value); 228 } 229 catch (const sdbusplus::exception::SdBusError& e) 230 { 231 phosphor::logging::log<phosphor::logging::level::INFO>( 232 "ERROR: setProperty"); 233 return -1; 234 } 235 236 return 0; 237 } 238 239 int8_t Manufacturing::disablePidControlService(const bool disable) 240 { 241 try 242 { 243 auto dbus = getSdBus(); 244 auto method = dbus->new_method_call(systemDService, systemDObjPath, 245 systemDMgrIntf, 246 disable ? "StopUnit" : "StartUnit"); 247 method.append(pidControlService, "replace"); 248 auto reply = dbus->call(method); 249 } 250 catch (const sdbusplus::exception::SdBusError& e) 251 { 252 phosphor::logging::log<phosphor::logging::level::INFO>( 253 "ERROR: phosphor-pid-control service start or stop failed"); 254 return -1; 255 } 256 return 0; 257 } 258 259 ipmi::RspType<uint8_t, // Signal value 260 std::optional<uint16_t> // Fan tach value 261 > 262 appMTMGetSignal(boost::asio::yield_context yield, uint8_t signalTypeByte, 263 uint8_t instance, uint8_t actionByte) 264 { 265 // mfg filter logic is used to allow MTM get signal command only in 266 // manfacturing mode. 267 268 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte); 269 SmActionGet action = static_cast<SmActionGet>(actionByte); 270 271 switch (signalType) 272 { 273 case SmSignalGet::smFanPwmGet: 274 { 275 ipmi::Value reply; 276 std::string fullPath = fanPwmPath + std::to_string(instance + 1); 277 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value", 278 &reply) < 0) 279 { 280 return ipmi::responseInvalidFieldRequest(); 281 } 282 double* doubleVal = std::get_if<double>(&reply); 283 if (doubleVal == nullptr) 284 { 285 return ipmi::responseUnspecifiedError(); 286 } 287 uint8_t sensorVal = std::round(*doubleVal); 288 resetMtmTimer(yield); 289 return ipmi::responseSuccess(sensorVal, std::nullopt); 290 } 291 break; 292 case SmSignalGet::smFanTachometerGet: 293 { 294 auto sdbusp = getSdBus(); 295 boost::system::error_code ec; 296 using objFlatMap = boost::container::flat_map< 297 std::string, boost::container::flat_map< 298 std::string, std::vector<std::string>>>; 299 300 auto flatMap = sdbusp->yield_method_call<objFlatMap>( 301 yield, ec, "xyz.openbmc_project.ObjectMapper", 302 "/xyz/openbmc_project/object_mapper", 303 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 304 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf}); 305 if (ec) 306 { 307 phosphor::logging::log<phosphor::logging::level::ERR>( 308 "Failed to query fan tach sub tree objects"); 309 return ipmi::responseUnspecifiedError(); 310 } 311 if (instance >= flatMap.size()) 312 { 313 return ipmi::responseInvalidFieldRequest(); 314 } 315 auto itr = flatMap.nth(instance); 316 ipmi::Value reply; 317 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value", 318 &reply) < 0) 319 { 320 return ipmi::responseInvalidFieldRequest(); 321 } 322 323 double* doubleVal = std::get_if<double>(&reply); 324 if (doubleVal == nullptr) 325 { 326 return ipmi::responseUnspecifiedError(); 327 } 328 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT; 329 std::optional<uint16_t> fanTach = std::round(*doubleVal); 330 331 resetMtmTimer(yield); 332 return ipmi::responseSuccess(sensorVal, fanTach); 333 } 334 break; 335 case SmSignalGet::smIdentifyButton: 336 { 337 if (action == SmActionGet::revert || action == SmActionGet::ignore) 338 { 339 // ButtonMasked property is not supported for ID button as it is 340 // unnecessary. Hence if requested for revert / ignore, override 341 // it to sample action to make tools happy. 342 action = SmActionGet::sample; 343 } 344 // fall-through 345 } 346 case SmSignalGet::smResetButton: 347 case SmSignalGet::smPowerButton: 348 case SmSignalGet::smNMIButton: 349 { 350 std::string path; 351 if (getGpioPathForSmSignal(signalType, path) < 0) 352 { 353 return ipmi::responseInvalidFieldRequest(); 354 } 355 356 switch (action) 357 { 358 case SmActionGet::sample: 359 phosphor::logging::log<phosphor::logging::level::INFO>( 360 "case SmActionGet::sample"); 361 break; 362 case SmActionGet::ignore: 363 { 364 phosphor::logging::log<phosphor::logging::level::INFO>( 365 "case SmActionGet::ignore"); 366 if (mtm.setProperty(buttonService, path, buttonIntf, 367 "ButtonMasked", true) < 0) 368 { 369 return ipmi::responseUnspecifiedError(); 370 } 371 } 372 break; 373 case SmActionGet::revert: 374 { 375 phosphor::logging::log<phosphor::logging::level::INFO>( 376 "case SmActionGet::revert"); 377 if (mtm.setProperty(buttonService, path, buttonIntf, 378 "ButtonMasked", false) < 0) 379 { 380 return ipmi::responseUnspecifiedError(); 381 } 382 } 383 break; 384 385 default: 386 return ipmi::responseInvalidFieldRequest(); 387 break; 388 } 389 390 ipmi::Value reply; 391 if (mtm.getProperty(buttonService, path, buttonIntf, 392 "ButtonPressed", &reply) < 0) 393 { 394 return ipmi::responseUnspecifiedError(); 395 } 396 bool* valPtr = std::get_if<bool>(&reply); 397 if (valPtr == nullptr) 398 { 399 return ipmi::responseUnspecifiedError(); 400 } 401 resetMtmTimer(yield); 402 uint8_t sensorVal = *valPtr; 403 return ipmi::responseSuccess(sensorVal, std::nullopt); 404 } 405 break; 406 case SmSignalGet::smNcsiDiag: 407 { 408 constexpr const char* netBasePath = "/sys/class/net/eth"; 409 constexpr const char* carrierSuffix = "/carrier"; 410 std::ifstream netIfs(netBasePath + std::to_string(instance) + 411 carrierSuffix); 412 if (!netIfs.good()) 413 { 414 return ipmi::responseInvalidFieldRequest(); 415 } 416 std::string carrier; 417 netIfs >> carrier; 418 resetMtmTimer(yield); 419 return ipmi::responseSuccess( 420 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt); 421 } 422 break; 423 default: 424 return ipmi::responseInvalidFieldRequest(); 425 break; 426 } 427 } 428 429 ipmi::RspType<> appMTMSetSignal(boost::asio::yield_context yield, 430 uint8_t signalTypeByte, uint8_t instance, 431 uint8_t actionByte, 432 std::optional<uint8_t> pwmSpeed) 433 { 434 // mfg filter logic is used to allow MTM set signal command only in 435 // manfacturing mode. 436 437 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte); 438 SmActionSet action = static_cast<SmActionSet>(actionByte); 439 Cc retCode = ccSuccess; 440 int8_t ret = 0; 441 442 switch (signalType) 443 { 444 case SmSignalSet::smPowerFaultLed: 445 case SmSignalSet::smSystemReadyLed: 446 case SmSignalSet::smIdentifyLed: 447 switch (action) 448 { 449 case SmActionSet::forceDeasserted: 450 { 451 phosphor::logging::log<phosphor::logging::level::INFO>( 452 "case SmActionSet::forceDeasserted"); 453 454 retCode = ledStoreAndSet(signalType, std::string("Off")); 455 if (retCode != ccSuccess) 456 { 457 return ipmi::response(retCode); 458 } 459 mtm.revertTimer.start(revertTimeOut); 460 } 461 break; 462 case SmActionSet::forceAsserted: 463 { 464 phosphor::logging::log<phosphor::logging::level::INFO>( 465 "case SmActionSet::forceAsserted"); 466 467 retCode = ledStoreAndSet(signalType, std::string("On")); 468 if (retCode != ccSuccess) 469 { 470 return ipmi::response(retCode); 471 } 472 mtm.revertTimer.start(revertTimeOut); 473 if (SmSignalSet::smPowerFaultLed == signalType) 474 { 475 // Deassert "system ready" 476 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed, 477 std::string("Off")); 478 } 479 else if (SmSignalSet::smSystemReadyLed == signalType) 480 { 481 // Deassert "fault led" 482 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed, 483 std::string("Off")); 484 } 485 } 486 break; 487 case SmActionSet::revert: 488 { 489 phosphor::logging::log<phosphor::logging::level::INFO>( 490 "case SmActionSet::revert"); 491 retCode = ledRevert(signalType); 492 } 493 break; 494 default: 495 { 496 return ipmi::responseInvalidFieldRequest(); 497 } 498 } 499 break; 500 case SmSignalSet::smFanPowerSpeed: 501 { 502 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed)) 503 { 504 return ipmi::responseReqDataLenInvalid(); 505 } 506 507 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100)) 508 { 509 return ipmi::responseInvalidFieldRequest(); 510 } 511 512 uint8_t pwmValue = 0; 513 switch (action) 514 { 515 case SmActionSet::revert: 516 { 517 if (mtm.revertFanPWM) 518 { 519 ret = mtm.disablePidControlService(false); 520 if (ret < 0) 521 { 522 return ipmi::responseUnspecifiedError(); 523 } 524 mtm.revertFanPWM = false; 525 } 526 } 527 break; 528 case SmActionSet::forceAsserted: 529 { 530 pwmValue = *pwmSpeed; 531 } // fall-through 532 case SmActionSet::forceDeasserted: 533 { 534 if (!mtm.revertFanPWM) 535 { 536 ret = mtm.disablePidControlService(true); 537 if (ret < 0) 538 { 539 return ipmi::responseUnspecifiedError(); 540 } 541 mtm.revertFanPWM = true; 542 } 543 mtm.revertTimer.start(revertTimeOut); 544 std::string fanPwmInstancePath = 545 fanPwmPath + std::to_string(instance + 1); 546 547 ret = 548 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf, 549 "Value", static_cast<double>(pwmValue)); 550 if (ret < 0) 551 { 552 return ipmi::responseUnspecifiedError(); 553 } 554 } 555 break; 556 default: 557 { 558 return ipmi::responseInvalidFieldRequest(); 559 } 560 } 561 } 562 break; 563 default: 564 { 565 return ipmi::responseInvalidFieldRequest(); 566 } 567 } 568 if (retCode == ccSuccess) 569 { 570 resetMtmTimer(yield); 571 } 572 return ipmi::response(retCode); 573 } 574 575 ipmi::RspType<> mtmKeepAlive(boost::asio::yield_context yield, uint8_t reserved, 576 const std::array<char, 5>& intentionalSignature) 577 { 578 // mfg filter logic is used to allow MTM keep alive command only in 579 // manfacturing mode 580 581 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'}; 582 if (intentionalSignature != signatureOk || reserved != 0) 583 { 584 return ipmi::responseInvalidFieldRequest(); 585 } 586 return ipmi::response(resetMtmTimer(yield)); 587 } 588 589 static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd) 590 { 591 return (netFn << 8) | cmd; 592 } 593 594 ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request) 595 { 596 // Restricted commands, must be executed only in Manufacturing mode 597 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd)) 598 { 599 // i2c master write read command needs additional checking 600 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead): 601 if (request->payload.size() > 4) 602 { 603 // Allow write data count > 1, only if it is in MFG mode 604 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable) 605 { 606 return ipmi::ccInsufficientPrivilege; 607 } 608 } 609 break; 610 case makeCmdKey(ipmi::netFnOemOne, 611 ipmi::intel::general::cmdGetSmSignal): 612 case makeCmdKey(ipmi::netFnOemOne, 613 ipmi::intel::general::cmdSetSmSignal): 614 case makeCmdKey(ipmi::netFnOemOne, 615 ipmi::intel::general::cmdMtmKeepAlive): 616 case makeCmdKey(ipmi::netFnOemOne, 617 ipmi::intel::general::cmdSetManufacturingData): 618 case makeCmdKey(ipmi::netFnOemOne, 619 ipmi::intel::general::cmdGetManufacturingData): 620 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData): 621 622 // Check for MTM mode 623 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable) 624 { 625 return ipmi::ccInvalidCommand; 626 } 627 } 628 return ipmi::ccSuccess; 629 } 630 631 static constexpr uint8_t maxEthSize = 6; 632 static constexpr uint8_t maxSupportedEth = 3; 633 static constexpr const char* factoryEthAddrBaseFileName = 634 "/var/sofs/factory-settings/network/mac/eth"; 635 636 ipmi::RspType<> setManufacturingData(boost::asio::yield_context yield, 637 uint8_t dataType, 638 std::array<uint8_t, maxEthSize> ethData) 639 { 640 // mfg filter logic will restrict this command executing only in mfg mode. 641 if (dataType >= maxSupportedEth) 642 { 643 return ipmi::responseParmOutOfRange(); 644 } 645 646 constexpr uint8_t invalidData = 0; 647 constexpr uint8_t validData = 1; 648 constexpr uint8_t ethAddrStrSize = 649 19; // XX:XX:XX:XX:XX:XX + \n + null termination; 650 std::vector<uint8_t> buff(ethAddrStrSize); 651 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize, 652 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0), 653 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4), 654 ethData.at(5)); 655 std::ofstream oEthFile(factoryEthAddrBaseFileName + 656 std::to_string(dataType), 657 std::ofstream::out); 658 if (!oEthFile.good()) 659 { 660 return ipmi::responseUnspecifiedError(); 661 } 662 663 oEthFile << reinterpret_cast<char*>(buff.data()); 664 oEthFile << fflush; 665 oEthFile.close(); 666 667 resetMtmTimer(yield); 668 return ipmi::responseSuccess(); 669 } 670 671 ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>> 672 getManufacturingData(boost::asio::yield_context yield, uint8_t dataType) 673 { 674 // mfg filter logic will restrict this command executing only in mfg mode. 675 if (dataType >= maxSupportedEth) 676 { 677 return ipmi::responseParmOutOfRange(); 678 } 679 std::array<uint8_t, maxEthSize> ethData{0}; 680 constexpr uint8_t invalidData = 0; 681 constexpr uint8_t validData = 1; 682 683 std::ifstream iEthFile(factoryEthAddrBaseFileName + 684 std::to_string(dataType), 685 std::ifstream::in); 686 if (!iEthFile.good()) 687 { 688 return ipmi::responseSuccess(invalidData, ethData); 689 } 690 std::string ethStr; 691 iEthFile >> ethStr; 692 uint8_t* data = ethData.data(); 693 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", 694 data, (data + 1), (data + 2), (data + 3), (data + 4), 695 (data + 5)); 696 697 resetMtmTimer(yield); 698 return ipmi::responseSuccess(validData, ethData); 699 } 700 701 /** @brief implements slot master write read IPMI command which can be used for 702 * low-level I2C/SMBus write, read or write-read access for PCIE slots 703 * @param reserved - skip 6 bit 704 * @param addressType - address type 705 * @param bbSlotNum - baseboard slot number 706 * @param riserSlotNum - riser slot number 707 * @param reserved2 - skip 2 bit 708 * @param slaveAddr - slave address 709 * @param readCount - number of bytes to be read 710 * @param writeData - data to be written 711 * 712 * @returns IPMI completion code plus response data 713 */ 714 ipmi::RspType<std::vector<uint8_t>> 715 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType, 716 uint3_t bbSlotNum, uint3_t riserSlotNum, 717 uint2_t resvered2, uint8_t slaveAddr, 718 uint8_t readCount, std::vector<uint8_t> writeData) 719 { 720 const size_t writeCount = writeData.size(); 721 std::string i2cBus; 722 if (addressType == slotAddressTypeBus) 723 { 724 std::string path = "/dev/i2c-mux/Riser_" + 725 std::to_string(static_cast<uint8_t>(bbSlotNum)) + 726 "_Mux/Pcie_Slot_" + 727 std::to_string(static_cast<uint8_t>(riserSlotNum)); 728 729 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path)) 730 { 731 i2cBus = std::filesystem::read_symlink(path); 732 } 733 else 734 { 735 phosphor::logging::log<phosphor::logging::level::ERR>( 736 "Master write read command: Cannot get BusID"); 737 return ipmi::responseInvalidFieldRequest(); 738 } 739 } 740 else if (addressType == slotAddressTypeUniqueid) 741 { 742 i2cBus = "/dev/i2c-" + 743 std::to_string(static_cast<uint8_t>(bbSlotNum) | 744 (static_cast<uint8_t>(riserSlotNum) << 3)); 745 } 746 else 747 { 748 phosphor::logging::log<phosphor::logging::level::ERR>( 749 "Master write read command: invalid request"); 750 return ipmi::responseInvalidFieldRequest(); 751 } 752 753 // Allow single byte write as it is offset byte to read the data, rest allow 754 // only in MFG mode. 755 if (writeCount > 1) 756 { 757 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable) 758 { 759 return ipmi::responseInsufficientPrivilege(); 760 } 761 } 762 763 if (readCount > slotI2CMaxReadSize) 764 { 765 phosphor::logging::log<phosphor::logging::level::ERR>( 766 "Master write read command: Read count exceeds limit"); 767 return ipmi::responseParmOutOfRange(); 768 } 769 770 if (!readCount && !writeCount) 771 { 772 phosphor::logging::log<phosphor::logging::level::ERR>( 773 "Master write read command: Read & write count are 0"); 774 return ipmi::responseInvalidFieldRequest(); 775 } 776 777 std::vector<uint8_t> readBuf(readCount); 778 779 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf); 780 if (retI2C != ipmi::ccSuccess) 781 { 782 return ipmi::response(retI2C); 783 } 784 785 return ipmi::responseSuccess(readBuf); 786 } 787 788 ipmi::RspType<> clearCMOS() 789 { 790 // There is an i2c device on bus 4, the slave address is 0x70. Based on the 791 // spec, writing 0x1 to address 0x60 on this device, will trigger the clear 792 // CMOS action. 793 constexpr uint8_t slaveAddr = 0x70; 794 std::string i2cBus = "/dev/i2c-4"; 795 std::vector<uint8_t> writeData = {0x60, 0x1}; 796 std::vector<uint8_t> readBuf(0); 797 798 // TODO Needs to enhance the below security checking 799 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable) 800 { 801 return ipmi::responseInsufficientPrivilege(); 802 } 803 804 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf); 805 return ipmi::response(retI2C); 806 } 807 } // namespace ipmi 808 809 void register_mtm_commands() __attribute__((constructor)); 810 void register_mtm_commands() 811 { 812 // <Get SM Signal> 813 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 814 ipmi::intel::general::cmdGetSmSignal, 815 ipmi::Privilege::Admin, ipmi::appMTMGetSignal); 816 817 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 818 ipmi::intel::general::cmdSetSmSignal, 819 ipmi::Privilege::Admin, ipmi::appMTMSetSignal); 820 821 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 822 ipmi::intel::general::cmdMtmKeepAlive, 823 ipmi::Privilege::Admin, ipmi::mtmKeepAlive); 824 825 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 826 ipmi::intel::general::cmdSetManufacturingData, 827 ipmi::Privilege::Admin, ipmi::setManufacturingData); 828 829 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 830 ipmi::intel::general::cmdGetManufacturingData, 831 ipmi::Privilege::Admin, ipmi::getManufacturingData); 832 833 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 834 ipmi::intel::general::cmdSlotI2CMasterWriteRead, 835 ipmi::Privilege::Admin, 836 ipmi::appSlotI2CMasterWriteRead); 837 838 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform, 839 ipmi::intel::platform::cmdClearCMOS, 840 ipmi::Privilege::Admin, ipmi::clearCMOS); 841 842 ipmi::registerFilter(ipmi::prioOemBase, 843 [](ipmi::message::Request::ptr request) { 844 return ipmi::mfgFilterMessage(request); 845 }); 846 } 847