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