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 <linux/input.h> 18 19 #include <boost/algorithm/string.hpp> 20 #include <boost/container/flat_map.hpp> 21 #include <ipmid/api.hpp> 22 #include <manufacturingcommands.hpp> 23 #include <oemcommands.hpp> 24 25 #include <filesystem> 26 #include <fstream> 27 28 namespace ipmi 29 { 30 31 Manufacturing mtm; 32 33 static auto revertTimeOut = 34 std::chrono::duration_cast<std::chrono::microseconds>( 35 std::chrono::seconds(60)); // 1 minute timeout 36 37 static constexpr uint8_t slotAddressTypeBus = 0; 38 static constexpr uint8_t slotAddressTypeUniqueid = 1; 39 static constexpr uint8_t slotI2CMaxReadSize = 35; 40 41 static constexpr const char* callbackMgrService = 42 "xyz.openbmc_project.CallbackManager"; 43 static constexpr const char* callbackMgrIntf = 44 "xyz.openbmc_project.CallbackManager"; 45 static constexpr const char* callbackMgrObjPath = 46 "/xyz/openbmc_project/CallbackManager"; 47 static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate"; 48 49 const static constexpr char* systemDService = "org.freedesktop.systemd1"; 50 const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1"; 51 const static constexpr char* systemDMgrIntf = 52 "org.freedesktop.systemd1.Manager"; 53 const static constexpr char* pidControlService = "phosphor-pid-control.service"; 54 55 static inline Cc resetMtmTimer(ipmi::Context::ptr ctx) 56 { 57 boost::system::error_code ec; 58 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService, 59 specialModeObjPath, specialModeIntf, 60 "ResetTimer"); 61 if (ec) 62 { 63 phosphor::logging::log<phosphor::logging::level::ERR>( 64 "Failed to reset the manufacturing mode timer"); 65 return ccUnspecifiedError; 66 } 67 return ccSuccess; 68 } 69 70 int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path) 71 { 72 switch (signal) 73 { 74 case SmSignalGet::smPowerButton: 75 path = "/xyz/openbmc_project/chassis/buttons/power"; 76 break; 77 case SmSignalGet::smResetButton: 78 path = "/xyz/openbmc_project/chassis/buttons/reset"; 79 break; 80 case SmSignalGet::smNMIButton: 81 path = "/xyz/openbmc_project/chassis/buttons/nmi"; 82 break; 83 case SmSignalGet::smIdentifyButton: 84 path = "/xyz/openbmc_project/chassis/buttons/id"; 85 break; 86 default: 87 return -1; 88 break; 89 } 90 return 0; 91 } 92 93 ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState) 94 { 95 LedProperty* ledProp = mtm.findLedProperty(signal); 96 if (ledProp == nullptr) 97 { 98 return IPMI_CC_INVALID_FIELD_REQUEST; 99 } 100 101 std::string ledName = ledProp->getName(); 102 std::string ledService = ledServicePrefix + ledName; 103 std::string ledPath = ledPathPrefix + ledName; 104 ipmi::Value presentState; 105 106 if (false == ledProp->getLock()) 107 { 108 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf, 109 "State", &presentState) != 0) 110 { 111 return IPMI_CC_UNSPECIFIED_ERROR; 112 } 113 ledProp->setPrevState(std::get<std::string>(presentState)); 114 ledProp->setLock(true); 115 if (signal == SmSignalSet::smPowerFaultLed || 116 signal == SmSignalSet::smSystemReadyLed) 117 { 118 mtm.revertLedCallback = true; 119 } 120 } 121 if (mtm.setProperty(ledService, ledPath, ledIntf, "State", 122 ledStateStr + setState) != 0) 123 { 124 return IPMI_CC_UNSPECIFIED_ERROR; 125 } 126 return IPMI_CC_OK; 127 } 128 129 ipmi_ret_t ledRevert(SmSignalSet signal) 130 { 131 LedProperty* ledProp = mtm.findLedProperty(signal); 132 if (ledProp == nullptr) 133 { 134 return IPMI_CC_INVALID_FIELD_REQUEST; 135 } 136 if (true == ledProp->getLock()) 137 { 138 ledProp->setLock(false); 139 if (signal == SmSignalSet::smPowerFaultLed || 140 signal == SmSignalSet::smSystemReadyLed) 141 { 142 try 143 { 144 ipmi::method_no_args::callDbusMethod( 145 *getSdBus(), callbackMgrService, callbackMgrObjPath, 146 callbackMgrIntf, retriggerLedUpdate); 147 } 148 catch (const sdbusplus::exception_t& e) 149 { 150 return IPMI_CC_UNSPECIFIED_ERROR; 151 } 152 mtm.revertLedCallback = false; 153 } 154 else 155 { 156 std::string ledName = ledProp->getName(); 157 std::string ledService = ledServicePrefix + ledName; 158 std::string ledPath = ledPathPrefix + ledName; 159 if (mtm.setProperty(ledService, ledPath, ledIntf, "State", 160 ledProp->getPrevState()) != 0) 161 { 162 return IPMI_CC_UNSPECIFIED_ERROR; 163 } 164 } 165 } 166 return IPMI_CC_OK; 167 } 168 169 void Manufacturing::initData() 170 { 171 ledPropertyList.push_back( 172 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber")); 173 ledPropertyList.push_back( 174 LedProperty(SmSignalSet::smSystemReadyLed, "status_green")); 175 ledPropertyList.push_back( 176 LedProperty(SmSignalSet::smIdentifyLed, "identify")); 177 } 178 179 void Manufacturing::revertTimerHandler() 180 { 181 182 #ifdef BMC_VALIDATION_UNSECURE_FEATURE 183 if (mtm.getMfgMode() == SpecialMode::valUnsecure) 184 { 185 // Don't revert the behaviour for validation unsecure mode. 186 return; 187 } 188 #endif 189 if (revertFanPWM) 190 { 191 revertFanPWM = false; 192 disablePidControlService(false); 193 } 194 195 if (mtmTestBeepFd != -1) 196 { 197 ::close(mtmTestBeepFd); 198 mtmTestBeepFd = -1; 199 } 200 201 for (const auto& ledProperty : ledPropertyList) 202 { 203 const std::string& ledName = ledProperty.getName(); 204 if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg) 205 { 206 // Don't revert the behaviour for manufacturing mode 207 continue; 208 } 209 ledRevert(ledProperty.getSignal()); 210 } 211 } 212 213 Manufacturing::Manufacturing() : 214 revertTimer([&](void) { revertTimerHandler(); }) 215 { 216 initData(); 217 } 218 219 int8_t Manufacturing::getProperty(const std::string& service, 220 const std::string& path, 221 const std::string& interface, 222 const std::string& propertyName, 223 ipmi::Value* reply) 224 { 225 try 226 { 227 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface, 228 propertyName); 229 } 230 catch (const sdbusplus::exception_t& e) 231 { 232 phosphor::logging::log<phosphor::logging::level::INFO>( 233 "ERROR: getProperty"); 234 return -1; 235 } 236 237 return 0; 238 } 239 240 int8_t Manufacturing::setProperty(const std::string& service, 241 const std::string& path, 242 const std::string& interface, 243 const std::string& propertyName, 244 ipmi::Value value) 245 { 246 try 247 { 248 ipmi::setDbusProperty(*getSdBus(), service, path, interface, 249 propertyName, value); 250 } 251 catch (const sdbusplus::exception_t& e) 252 { 253 phosphor::logging::log<phosphor::logging::level::INFO>( 254 "ERROR: setProperty"); 255 return -1; 256 } 257 258 return 0; 259 } 260 261 int8_t Manufacturing::disablePidControlService(const bool disable) 262 { 263 try 264 { 265 auto dbus = getSdBus(); 266 auto method = dbus->new_method_call(systemDService, systemDObjPath, 267 systemDMgrIntf, 268 disable ? "StopUnit" : "StartUnit"); 269 method.append(pidControlService, "replace"); 270 auto reply = dbus->call(method); 271 } 272 catch (const sdbusplus::exception_t& e) 273 { 274 phosphor::logging::log<phosphor::logging::level::INFO>( 275 "ERROR: phosphor-pid-control service start or stop failed"); 276 return -1; 277 } 278 return 0; 279 } 280 281 ipmi::RspType<uint8_t, // Signal value 282 std::optional<uint16_t> // Fan tach value 283 > 284 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte, 285 uint8_t instance, uint8_t actionByte) 286 { 287 // mfg filter logic is used to allow MTM get signal command only in 288 // manfacturing mode. 289 290 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte); 291 SmActionGet action = static_cast<SmActionGet>(actionByte); 292 293 switch (signalType) 294 { 295 case SmSignalGet::smChassisIntrusion: 296 { 297 ipmi::Value reply; 298 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf, 299 "Status", &reply) < 0) 300 { 301 return ipmi::responseInvalidFieldRequest(); 302 } 303 std::string* intrusionStatus = std::get_if<std::string>(&reply); 304 if (!intrusionStatus) 305 { 306 return ipmi::responseUnspecifiedError(); 307 } 308 309 uint8_t status = 0; 310 if (!intrusionStatus->compare("Normal")) 311 { 312 status = static_cast<uint8_t>(IntrusionStatus::normal); 313 } 314 else if (!intrusionStatus->compare("HardwareIntrusion")) 315 { 316 status = 317 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion); 318 } 319 else if (!intrusionStatus->compare("TamperingDetected")) 320 { 321 status = 322 static_cast<uint8_t>(IntrusionStatus::tamperingDetected); 323 } 324 else 325 { 326 return ipmi::responseUnspecifiedError(); 327 } 328 return ipmi::responseSuccess(status, std::nullopt); 329 } 330 case SmSignalGet::smFanPwmGet: 331 { 332 ipmi::Value reply; 333 std::string fullPath = fanPwmPath + std::to_string(instance + 1); 334 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value", 335 &reply) < 0) 336 { 337 return ipmi::responseInvalidFieldRequest(); 338 } 339 double* doubleVal = std::get_if<double>(&reply); 340 if (doubleVal == nullptr) 341 { 342 return ipmi::responseUnspecifiedError(); 343 } 344 uint8_t sensorVal = std::round(*doubleVal); 345 resetMtmTimer(ctx); 346 return ipmi::responseSuccess(sensorVal, std::nullopt); 347 } 348 break; 349 case SmSignalGet::smFanTachometerGet: 350 { 351 boost::system::error_code ec; 352 using objFlatMap = boost::container::flat_map< 353 std::string, boost::container::flat_map< 354 std::string, std::vector<std::string>>>; 355 356 auto flatMap = ctx->bus->yield_method_call<objFlatMap>( 357 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper", 358 "/xyz/openbmc_project/object_mapper", 359 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 360 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf}); 361 if (ec) 362 { 363 phosphor::logging::log<phosphor::logging::level::ERR>( 364 "Failed to query fan tach sub tree objects"); 365 return ipmi::responseUnspecifiedError(); 366 } 367 if (instance >= flatMap.size()) 368 { 369 return ipmi::responseInvalidFieldRequest(); 370 } 371 auto itr = flatMap.nth(instance); 372 ipmi::Value reply; 373 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value", 374 &reply) < 0) 375 { 376 return ipmi::responseInvalidFieldRequest(); 377 } 378 379 double* doubleVal = std::get_if<double>(&reply); 380 if (doubleVal == nullptr) 381 { 382 return ipmi::responseUnspecifiedError(); 383 } 384 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT; 385 std::optional<uint16_t> fanTach = std::round(*doubleVal); 386 387 resetMtmTimer(ctx); 388 return ipmi::responseSuccess(sensorVal, fanTach); 389 } 390 break; 391 case SmSignalGet::smIdentifyButton: 392 { 393 if (action == SmActionGet::revert || action == SmActionGet::ignore) 394 { 395 // ButtonMasked property is not supported for ID button as it is 396 // unnecessary. Hence if requested for revert / ignore, override 397 // it to sample action to make tools happy. 398 action = SmActionGet::sample; 399 } 400 // fall-through 401 } 402 case SmSignalGet::smResetButton: 403 case SmSignalGet::smPowerButton: 404 case SmSignalGet::smNMIButton: 405 { 406 std::string path; 407 if (getGpioPathForSmSignal(signalType, path) < 0) 408 { 409 return ipmi::responseInvalidFieldRequest(); 410 } 411 412 switch (action) 413 { 414 case SmActionGet::sample: 415 phosphor::logging::log<phosphor::logging::level::INFO>( 416 "case SmActionGet::sample"); 417 break; 418 case SmActionGet::ignore: 419 { 420 phosphor::logging::log<phosphor::logging::level::INFO>( 421 "case SmActionGet::ignore"); 422 if (mtm.setProperty(buttonService, path, buttonIntf, 423 "ButtonMasked", true) < 0) 424 { 425 return ipmi::responseUnspecifiedError(); 426 } 427 } 428 break; 429 case SmActionGet::revert: 430 { 431 phosphor::logging::log<phosphor::logging::level::INFO>( 432 "case SmActionGet::revert"); 433 if (mtm.setProperty(buttonService, path, buttonIntf, 434 "ButtonMasked", false) < 0) 435 { 436 return ipmi::responseUnspecifiedError(); 437 } 438 } 439 break; 440 441 default: 442 return ipmi::responseInvalidFieldRequest(); 443 break; 444 } 445 446 ipmi::Value reply; 447 if (mtm.getProperty(buttonService, path, buttonIntf, 448 "ButtonPressed", &reply) < 0) 449 { 450 return ipmi::responseUnspecifiedError(); 451 } 452 bool* valPtr = std::get_if<bool>(&reply); 453 if (valPtr == nullptr) 454 { 455 return ipmi::responseUnspecifiedError(); 456 } 457 resetMtmTimer(ctx); 458 uint8_t sensorVal = *valPtr; 459 return ipmi::responseSuccess(sensorVal, std::nullopt); 460 } 461 break; 462 case SmSignalGet::smNcsiDiag: 463 { 464 constexpr const char* netBasePath = "/sys/class/net/eth"; 465 constexpr const char* carrierSuffix = "/carrier"; 466 std::ifstream netIfs(netBasePath + std::to_string(instance) + 467 carrierSuffix); 468 if (!netIfs.good()) 469 { 470 return ipmi::responseInvalidFieldRequest(); 471 } 472 std::string carrier; 473 netIfs >> carrier; 474 resetMtmTimer(ctx); 475 return ipmi::responseSuccess( 476 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt); 477 } 478 break; 479 default: 480 return ipmi::responseInvalidFieldRequest(); 481 break; 482 } 483 } 484 485 ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte, 486 uint8_t instance, uint8_t actionByte, 487 std::optional<uint8_t> pwmSpeed) 488 { 489 // mfg filter logic is used to allow MTM set signal command only in 490 // manfacturing mode. 491 492 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte); 493 SmActionSet action = static_cast<SmActionSet>(actionByte); 494 Cc retCode = ccSuccess; 495 int8_t ret = 0; 496 497 switch (signalType) 498 { 499 case SmSignalSet::smPowerFaultLed: 500 case SmSignalSet::smSystemReadyLed: 501 case SmSignalSet::smIdentifyLed: 502 switch (action) 503 { 504 case SmActionSet::forceDeasserted: 505 { 506 phosphor::logging::log<phosphor::logging::level::INFO>( 507 "case SmActionSet::forceDeasserted"); 508 509 retCode = ledStoreAndSet(signalType, std::string("Off")); 510 if (retCode != ccSuccess) 511 { 512 return ipmi::response(retCode); 513 } 514 mtm.revertTimer.start(revertTimeOut); 515 } 516 break; 517 case SmActionSet::forceAsserted: 518 { 519 phosphor::logging::log<phosphor::logging::level::INFO>( 520 "case SmActionSet::forceAsserted"); 521 522 retCode = ledStoreAndSet(signalType, std::string("On")); 523 if (retCode != ccSuccess) 524 { 525 return ipmi::response(retCode); 526 } 527 mtm.revertTimer.start(revertTimeOut); 528 if (SmSignalSet::smPowerFaultLed == signalType) 529 { 530 // Deassert "system ready" 531 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed, 532 std::string("Off")); 533 } 534 else if (SmSignalSet::smSystemReadyLed == signalType) 535 { 536 // Deassert "fault led" 537 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed, 538 std::string("Off")); 539 } 540 } 541 break; 542 case SmActionSet::revert: 543 { 544 phosphor::logging::log<phosphor::logging::level::INFO>( 545 "case SmActionSet::revert"); 546 retCode = ledRevert(signalType); 547 } 548 break; 549 default: 550 { 551 return ipmi::responseInvalidFieldRequest(); 552 } 553 } 554 break; 555 case SmSignalSet::smFanPowerSpeed: 556 { 557 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed)) 558 { 559 return ipmi::responseReqDataLenInvalid(); 560 } 561 562 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100)) 563 { 564 return ipmi::responseInvalidFieldRequest(); 565 } 566 567 uint8_t pwmValue = 0; 568 switch (action) 569 { 570 case SmActionSet::revert: 571 { 572 if (mtm.revertFanPWM) 573 { 574 ret = mtm.disablePidControlService(false); 575 if (ret < 0) 576 { 577 return ipmi::responseUnspecifiedError(); 578 } 579 mtm.revertFanPWM = false; 580 } 581 } 582 break; 583 case SmActionSet::forceAsserted: 584 { 585 pwmValue = *pwmSpeed; 586 } // fall-through 587 case SmActionSet::forceDeasserted: 588 { 589 if (!mtm.revertFanPWM) 590 { 591 ret = mtm.disablePidControlService(true); 592 if (ret < 0) 593 { 594 return ipmi::responseUnspecifiedError(); 595 } 596 mtm.revertFanPWM = true; 597 } 598 mtm.revertTimer.start(revertTimeOut); 599 std::string fanPwmInstancePath = 600 fanPwmPath + std::to_string(instance + 1); 601 602 ret = 603 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf, 604 "Value", static_cast<double>(pwmValue)); 605 if (ret < 0) 606 { 607 return ipmi::responseUnspecifiedError(); 608 } 609 } 610 break; 611 default: 612 { 613 return ipmi::responseInvalidFieldRequest(); 614 } 615 } 616 } 617 break; 618 case SmSignalSet::smSpeaker: 619 { 620 phosphor::logging::log<phosphor::logging::level::INFO>( 621 "Performing Speaker SmActionSet", 622 phosphor::logging::entry("ACTION=%d", 623 static_cast<uint8_t>(action))); 624 switch (action) 625 { 626 case SmActionSet::forceAsserted: 627 { 628 char beepDevName[] = "/dev/input/event0"; 629 if (mtm.mtmTestBeepFd != -1) 630 { 631 phosphor::logging::log<phosphor::logging::level::INFO>( 632 "mtm beep device is opened already!"); 633 // returning success as already beep is in progress 634 return ipmi::response(retCode); 635 } 636 637 if ((mtm.mtmTestBeepFd = 638 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0) 639 { 640 phosphor::logging::log<phosphor::logging::level::ERR>( 641 "Failed to open input device"); 642 return ipmi::responseUnspecifiedError(); 643 } 644 645 struct input_event event; 646 event.type = EV_SND; 647 event.code = SND_TONE; 648 event.value = 2000; 649 650 if (::write(mtm.mtmTestBeepFd, &event, 651 sizeof(struct input_event)) != 652 sizeof(struct input_event)) 653 { 654 phosphor::logging::log<phosphor::logging::level::ERR>( 655 "Failed to write a tone sound event"); 656 ::close(mtm.mtmTestBeepFd); 657 mtm.mtmTestBeepFd = -1; 658 return ipmi::responseUnspecifiedError(); 659 } 660 mtm.revertTimer.start(revertTimeOut); 661 } 662 break; 663 case SmActionSet::revert: 664 case SmActionSet::forceDeasserted: 665 { 666 if (mtm.mtmTestBeepFd != -1) 667 { 668 ::close(mtm.mtmTestBeepFd); 669 mtm.mtmTestBeepFd = -1; 670 } 671 } 672 break; 673 default: 674 { 675 return ipmi::responseInvalidFieldRequest(); 676 } 677 } 678 } 679 break; 680 case SmSignalSet::smDiskFaultLed: 681 { 682 boost::system::error_code ec; 683 using objPaths = std::vector<std::string>; 684 std::string driveBasePath = 685 "/xyz/openbmc_project/inventory/item/drive/"; 686 static constexpr const char* driveLedIntf = 687 "xyz.openbmc_project.Led.Group"; 688 static constexpr const char* hsbpService = 689 "xyz.openbmc_project.HsbpManager"; 690 691 auto driveList = ctx->bus->yield_method_call<objPaths>( 692 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper", 693 "/xyz/openbmc_project/object_mapper", 694 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 695 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf}); 696 if (ec) 697 { 698 phosphor::logging::log<phosphor::logging::level::ERR>( 699 "Failed to query HSBP drive sub tree objects"); 700 return ipmi::responseUnspecifiedError(); 701 } 702 std::string driveObjPath = 703 driveBasePath + "Drive_" + std::to_string(instance + 1); 704 if (std::find(driveList.begin(), driveList.end(), driveObjPath) == 705 driveList.end()) 706 { 707 return ipmi::responseInvalidFieldRequest(); 708 } 709 bool driveLedState = false; 710 switch (action) 711 { 712 case SmActionSet::forceAsserted: 713 { 714 driveLedState = true; 715 } 716 break; 717 case SmActionSet::revert: 718 { 719 driveLedState = false; 720 } 721 break; 722 case SmActionSet::forceDeasserted: 723 { 724 driveLedState = false; 725 } 726 break; 727 default: 728 { 729 return ipmi::responseInvalidFieldRequest(); 730 } 731 } 732 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf, 733 "Asserted", driveLedState); 734 if (ret < 0) 735 { 736 return ipmi::responseUnspecifiedError(); 737 } 738 } 739 break; 740 default: 741 { 742 return ipmi::responseInvalidFieldRequest(); 743 } 744 } 745 if (retCode == ccSuccess) 746 { 747 resetMtmTimer(ctx); 748 } 749 return ipmi::response(retCode); 750 } 751 752 ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved, 753 const std::array<char, 5>& intentionalSignature) 754 { 755 // mfg filter logic is used to allow MTM keep alive command only in 756 // manfacturing mode 757 758 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'}; 759 if (intentionalSignature != signatureOk || reserved != 0) 760 { 761 return ipmi::responseInvalidFieldRequest(); 762 } 763 return ipmi::response(resetMtmTimer(ctx)); 764 } 765 766 static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd) 767 { 768 return (netFn << 8) | cmd; 769 } 770 771 ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request) 772 { 773 // Restricted commands, must be executed only in Manufacturing mode 774 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd)) 775 { 776 // i2c master write read command needs additional checking 777 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead): 778 if (request->payload.size() > 4) 779 { 780 // Allow write data count > 1 only in Special mode 781 if (mtm.getMfgMode() == SpecialMode::none) 782 { 783 return ipmi::ccInsufficientPrivilege; 784 } 785 } 786 return ipmi::ccSuccess; 787 case makeCmdKey(ipmi::netFnOemOne, 788 ipmi::intel::general::cmdGetSmSignal): 789 case makeCmdKey(ipmi::netFnOemOne, 790 ipmi::intel::general::cmdSetSmSignal): 791 case makeCmdKey(ipmi::netFnOemOne, 792 ipmi::intel::general::cmdMtmKeepAlive): 793 case makeCmdKey(ipmi::netFnOemOne, 794 ipmi::intel::general::cmdSetManufacturingData): 795 case makeCmdKey(ipmi::netFnOemOne, 796 ipmi::intel::general::cmdGetManufacturingData): 797 case makeCmdKey(ipmi::intel::netFnGeneral, 798 ipmi::intel::general::cmdSetFITcLayout): 799 case makeCmdKey(ipmi::netFnOemOne, 800 ipmi::intel::general::cmdMTMBMCFeatureControl): 801 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData): 802 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS): 803 804 // Check for Special mode 805 if (mtm.getMfgMode() == SpecialMode::none) 806 { 807 return ipmi::ccInvalidCommand; 808 } 809 return ipmi::ccSuccess; 810 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry): 811 { 812 return ipmi::ccInvalidCommand; 813 } 814 } 815 return ipmi::ccSuccess; 816 } 817 818 static constexpr uint8_t maxEthSize = 6; 819 static constexpr uint8_t maxSupportedEth = 3; 820 static constexpr const char* factoryEthAddrBaseFileName = 821 "/var/sofs/factory-settings/network/mac/eth"; 822 823 using ObjectType = boost::container::flat_map< 824 std::string, boost::container::flat_map<std::string, DbusVariant>>; 825 using ManagedObjectType = 826 boost::container::flat_map<sdbusplus::message::object_path, ObjectType>; 827 828 bool findFruDevice(const std::shared_ptr<sdbusplus::asio::connection>& bus, 829 boost::asio::yield_context& yield, uint64_t& macOffset, 830 uint64_t& busNum, uint64_t& address) 831 { 832 boost::system::error_code ec; 833 834 // GetAll the objects under service FruDevice 835 ec = boost::system::errc::make_error_code(boost::system::errc::success); 836 auto obj = bus->yield_method_call<ManagedObjectType>( 837 yield, ec, "xyz.openbmc_project.EntityManager", "/", 838 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 839 if (ec) 840 { 841 phosphor::logging::log<phosphor::logging::level::ERR>( 842 "GetMangagedObjects failed", 843 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 844 return false; 845 } 846 847 for (const auto& [path, fru] : obj) 848 { 849 for (const auto& [intf, propMap] : fru) 850 { 851 if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard") 852 { 853 auto findBus = propMap.find("FruBus"); 854 auto findAddress = propMap.find("FruAddress"); 855 auto findMacOffset = propMap.find("MacOffset"); 856 if (findBus == propMap.end() || findAddress == propMap.end() || 857 findMacOffset == propMap.end()) 858 { 859 continue; 860 } 861 862 auto fruBus = std::get_if<uint64_t>(&findBus->second); 863 auto fruAddress = std::get_if<uint64_t>(&findAddress->second); 864 auto macFruOffset = 865 std::get_if<uint64_t>(&findMacOffset->second); 866 if (!fruBus || !fruAddress || !macFruOffset) 867 { 868 phosphor::logging::log<phosphor::logging::level::INFO>( 869 "ERROR: MotherBoard FRU config data type invalid, not " 870 "used"); 871 return false; 872 } 873 busNum = *fruBus; 874 address = *fruAddress; 875 macOffset = *macFruOffset; 876 return true; 877 } 878 } 879 } 880 return false; 881 } 882 883 static constexpr uint64_t fruEnd = 0xff; 884 // write rolls over within current page, need to keep mac within a page 885 static constexpr uint64_t fruPageSize = 0x8; 886 // MAC record struct: HEADER, MAC DATA, CheckSum 887 static constexpr uint64_t macRecordSize = maxEthSize + 2; 888 static_assert(fruPageSize >= macRecordSize, 889 "macRecordSize greater than eeprom page size"); 890 static constexpr uint8_t macHeader = 0x40; 891 // Calculate new checksum for fru info area 892 static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter, 893 std::vector<uint8_t>::const_iterator end) 894 { 895 constexpr int checksumMod = 256; 896 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0)); 897 return (checksumMod - sum) % checksumMod; 898 } 899 900 bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex, 901 std::array<uint8_t, maxEthSize>& ethData) 902 { 903 uint64_t macOffset = fruEnd; 904 uint64_t fruBus = 0; 905 uint64_t fruAddress = 0; 906 907 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress)) 908 { 909 phosphor::logging::log<phosphor::logging::level::INFO>( 910 "Found mac fru", 911 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)), 912 phosphor::logging::entry("ADDRESS=%d", 913 static_cast<uint8_t>(fruAddress))); 914 915 if (macOffset % fruPageSize) 916 { 917 macOffset = (macOffset / fruPageSize + 1) * fruPageSize; 918 } 919 macOffset += macIndex * fruPageSize; 920 if ((macOffset + macRecordSize) > fruEnd) 921 { 922 phosphor::logging::log<phosphor::logging::level::ERR>( 923 "ERROR: read fru mac failed, offset invalid"); 924 return false; 925 } 926 std::vector<uint8_t> writeData; 927 writeData.push_back(static_cast<uint8_t>(macOffset)); 928 std::vector<uint8_t> readBuf(macRecordSize); 929 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus); 930 ipmi::Cc retI2C = 931 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf); 932 if (retI2C == ipmi::ccSuccess) 933 { 934 uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend()); 935 if (cs == 0) 936 { 937 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data()); 938 return true; 939 } 940 } 941 } 942 return false; 943 } 944 945 ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex, 946 std::array<uint8_t, maxEthSize>& ethData) 947 { 948 uint64_t macOffset = fruEnd; 949 uint64_t fruBus = 0; 950 uint64_t fruAddress = 0; 951 952 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress)) 953 { 954 phosphor::logging::log<phosphor::logging::level::INFO>( 955 "Found mac fru", 956 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)), 957 phosphor::logging::entry("ADDRESS=%d", 958 static_cast<uint8_t>(fruAddress))); 959 960 if (macOffset % fruPageSize) 961 { 962 macOffset = (macOffset / fruPageSize + 1) * fruPageSize; 963 } 964 macOffset += macIndex * fruPageSize; 965 if ((macOffset + macRecordSize) > fruEnd) 966 { 967 phosphor::logging::log<phosphor::logging::level::ERR>( 968 "ERROR: write mac fru failed, offset invalid."); 969 return ipmi::ccParmOutOfRange; 970 } 971 std::vector<uint8_t> writeData; 972 writeData.reserve(macRecordSize + 1); // include start location 973 writeData.push_back(static_cast<uint8_t>(macOffset)); 974 writeData.push_back(macHeader); 975 std::for_each(ethData.cbegin(), ethData.cend(), 976 [&](uint8_t i) { writeData.push_back(i); }); 977 uint8_t macCheckSum = 978 calculateChecksum(++writeData.cbegin(), writeData.cend()); 979 writeData.push_back(macCheckSum); 980 981 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus); 982 std::vector<uint8_t> readBuf; 983 ipmi::Cc ret = 984 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf); 985 986 // prepare for read to detect chip is write protected 987 writeData.resize(1); 988 readBuf.resize(maxEthSize + 1); // include macHeader 989 990 switch (ret) 991 { 992 case ipmi::ccSuccess: 993 // Wait for internal write cycle to complete 994 // example: ATMEL 24c0x chip has Twr spec as 5ms 995 996 // Ideally we want yield wait, but currently following code 997 // crash with "thread not supported" 998 // boost::asio::deadline_timer timer( 999 // boost::asio::get_associated_executor(ctx->yield), 1000 // boost::posix_time::seconds(1)); 1001 // timer.async_wait(ctx->yield); 1002 // use usleep as temp WA 1003 usleep(5000); 1004 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, 1005 readBuf) == ipmi::ccSuccess) 1006 { 1007 if (std::equal(ethData.begin(), ethData.end(), 1008 ++readBuf.begin())) // skip macHeader 1009 { 1010 return ipmi::ccSuccess; 1011 } 1012 phosphor::logging::log<phosphor::logging::level::INFO>( 1013 "INFO: write mac fru verify failed, fru may be write " 1014 "protected."); 1015 } 1016 return ipmi::ccCommandNotAvailable; 1017 default: 1018 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, 1019 readBuf) == ipmi::ccSuccess) 1020 { 1021 phosphor::logging::log<phosphor::logging::level::INFO>( 1022 "INFO: write mac fru failed, but successfully read " 1023 "from fru, fru may be write protected."); 1024 return ipmi::ccCommandNotAvailable; 1025 } 1026 else // assume failure is due to no eeprom on board 1027 { 1028 phosphor::logging::log<phosphor::logging::level::ERR>( 1029 "ERROR: write mac fru failed, assume no eeprom is " 1030 "available."); 1031 } 1032 break; 1033 } 1034 } 1035 // no FRU eeprom found 1036 return ipmi::ccDestinationUnavailable; 1037 } 1038 1039 ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType, 1040 std::array<uint8_t, maxEthSize> ethData) 1041 { 1042 // mfg filter logic will restrict this command executing only in mfg 1043 // mode. 1044 if (dataType >= maxSupportedEth) 1045 { 1046 return ipmi::responseParmOutOfRange(); 1047 } 1048 1049 constexpr uint8_t invalidData = 0; 1050 constexpr uint8_t validData = 1; 1051 1052 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData); 1053 if (ret != ipmi::ccDestinationUnavailable) 1054 { 1055 resetMtmTimer(ctx); 1056 return response(ret); 1057 } 1058 1059 constexpr uint8_t ethAddrStrSize = 1060 19; // XX:XX:XX:XX:XX:XX + \n + null termination; 1061 std::vector<uint8_t> buff(ethAddrStrSize); 1062 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize, 1063 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0), 1064 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4), 1065 ethData.at(5)); 1066 std::ofstream oEthFile(factoryEthAddrBaseFileName + 1067 std::to_string(dataType), 1068 std::ofstream::out); 1069 if (!oEthFile.good()) 1070 { 1071 return ipmi::responseUnspecifiedError(); 1072 } 1073 1074 oEthFile << reinterpret_cast<char*>(buff.data()); 1075 oEthFile.flush(); 1076 oEthFile.close(); 1077 1078 resetMtmTimer(ctx); 1079 return ipmi::responseSuccess(); 1080 } 1081 1082 ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>> 1083 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType) 1084 { 1085 // mfg filter logic will restrict this command executing only in mfg 1086 // mode. 1087 if (dataType >= maxSupportedEth) 1088 { 1089 return ipmi::responseParmOutOfRange(); 1090 } 1091 std::array<uint8_t, maxEthSize> ethData{0}; 1092 constexpr uint8_t invalidData = 0; 1093 constexpr uint8_t validData = 1; 1094 1095 std::ifstream iEthFile(factoryEthAddrBaseFileName + 1096 std::to_string(dataType), 1097 std::ifstream::in); 1098 if (!iEthFile.good()) 1099 { 1100 if (readMacFromFru(ctx, dataType, ethData)) 1101 { 1102 resetMtmTimer(ctx); 1103 return ipmi::responseSuccess(validData, ethData); 1104 } 1105 return ipmi::responseSuccess(invalidData, ethData); 1106 } 1107 std::string ethStr; 1108 iEthFile >> ethStr; 1109 uint8_t* data = ethData.data(); 1110 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", 1111 data, (data + 1), (data + 2), (data + 3), (data + 4), 1112 (data + 5)); 1113 1114 resetMtmTimer(ctx); 1115 return ipmi::responseSuccess(validData, ethData); 1116 } 1117 1118 /** @brief implements slot master write read IPMI command which can be used 1119 * for low-level I2C/SMBus write, read or write-read access for PCIE slots 1120 * @param reserved - skip 6 bit 1121 * @param addressType - address type 1122 * @param bbSlotNum - baseboard slot number 1123 * @param riserSlotNum - riser slot number 1124 * @param reserved2 - skip 2 bit 1125 * @param slaveAddr - slave address 1126 * @param readCount - number of bytes to be read 1127 * @param writeData - data to be written 1128 * 1129 * @returns IPMI completion code plus response data 1130 */ 1131 ipmi::RspType<std::vector<uint8_t>> 1132 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType, 1133 uint3_t bbSlotNum, uint3_t riserSlotNum, 1134 uint2_t resvered2, uint8_t slaveAddr, 1135 uint8_t readCount, std::vector<uint8_t> writeData) 1136 { 1137 const size_t writeCount = writeData.size(); 1138 std::string i2cBus; 1139 if (addressType == slotAddressTypeBus) 1140 { 1141 std::string path = "/dev/i2c-mux/Riser_" + 1142 std::to_string(static_cast<uint8_t>(bbSlotNum)) + 1143 "_Mux/Pcie_Slot_" + 1144 std::to_string(static_cast<uint8_t>(riserSlotNum)); 1145 1146 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path)) 1147 { 1148 i2cBus = std::filesystem::read_symlink(path); 1149 } 1150 else 1151 { 1152 phosphor::logging::log<phosphor::logging::level::ERR>( 1153 "Master write read command: Cannot get BusID"); 1154 return ipmi::responseInvalidFieldRequest(); 1155 } 1156 } 1157 else if (addressType == slotAddressTypeUniqueid) 1158 { 1159 i2cBus = "/dev/i2c-" + 1160 std::to_string(static_cast<uint8_t>(bbSlotNum) | 1161 (static_cast<uint8_t>(riserSlotNum) << 3)); 1162 } 1163 else 1164 { 1165 phosphor::logging::log<phosphor::logging::level::ERR>( 1166 "Master write read command: invalid request"); 1167 return ipmi::responseInvalidFieldRequest(); 1168 } 1169 1170 // Allow single byte write as it is offset byte to read the data, rest 1171 // allow only in Special mode. 1172 if (writeCount > 1) 1173 { 1174 if (mtm.getMfgMode() == SpecialMode::none) 1175 { 1176 return ipmi::responseInsufficientPrivilege(); 1177 } 1178 } 1179 1180 if (readCount > slotI2CMaxReadSize) 1181 { 1182 phosphor::logging::log<phosphor::logging::level::ERR>( 1183 "Master write read command: Read count exceeds limit"); 1184 return ipmi::responseParmOutOfRange(); 1185 } 1186 1187 if (!readCount && !writeCount) 1188 { 1189 phosphor::logging::log<phosphor::logging::level::ERR>( 1190 "Master write read command: Read & write count are 0"); 1191 return ipmi::responseInvalidFieldRequest(); 1192 } 1193 1194 std::vector<uint8_t> readBuf(readCount); 1195 1196 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf); 1197 if (retI2C != ipmi::ccSuccess) 1198 { 1199 return ipmi::response(retI2C); 1200 } 1201 1202 return ipmi::responseSuccess(readBuf); 1203 } 1204 1205 ipmi::RspType<> clearCMOS() 1206 { 1207 // There is an i2c device on bus 4, the slave address is 0x38. Based on 1208 // the spec, writing 0x1 to address 0x61 on this device, will trigger 1209 // the clear CMOS action. 1210 constexpr uint8_t slaveAddr = 0x38; 1211 std::string i2cBus = "/dev/i2c-4"; 1212 std::vector<uint8_t> writeData = {0x61, 0x1}; 1213 std::vector<uint8_t> readBuf(0); 1214 1215 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf); 1216 return ipmi::response(retI2C); 1217 } 1218 1219 ipmi::RspType<> setFITcLayout(uint32_t layout) 1220 { 1221 static constexpr const char* factoryFITcLayout = 1222 "/var/sofs/factory-settings/layout/fitc"; 1223 std::filesystem::path fitcDir = 1224 std::filesystem::path(factoryFITcLayout).parent_path(); 1225 std::error_code ec; 1226 std::filesystem::create_directories(fitcDir, ec); 1227 if (ec) 1228 { 1229 return ipmi::responseUnspecifiedError(); 1230 } 1231 try 1232 { 1233 std::ofstream file(factoryFITcLayout); 1234 file << layout; 1235 file.flush(); 1236 file.close(); 1237 } 1238 catch (const std::exception& e) 1239 { 1240 return ipmi::responseUnspecifiedError(); 1241 } 1242 1243 return ipmi::responseSuccess(); 1244 } 1245 1246 static std::vector<std::string> 1247 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx) 1248 { 1249 boost::system::error_code ec; 1250 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>( 1251 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper", 1252 "/xyz/openbmc_project/object_mapper", 1253 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 1254 "/xyz/openbmc_project/inventory/system/board", 2, 1255 std::array<const char*, 1>{ 1256 "xyz.openbmc_project.Configuration.MctpConfiguration"}); 1257 if (ec) 1258 { 1259 throw std::runtime_error( 1260 "Failed to query configuration sub tree objects"); 1261 } 1262 return configPaths; 1263 } 1264 1265 static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx, 1266 const uint8_t enable, 1267 const std::string& serviceName, 1268 bool disableOrEnableUnitFiles = true) 1269 { 1270 constexpr bool runtimeOnly = false; 1271 constexpr bool force = false; 1272 1273 boost::system::error_code ec; 1274 switch (enable) 1275 { 1276 case ipmi::SupportedFeatureActions::stop: 1277 ctx->bus->yield_method_call(ctx->yield, ec, systemDService, 1278 systemDObjPath, systemDMgrIntf, 1279 "StopUnit", serviceName, "replace"); 1280 break; 1281 case ipmi::SupportedFeatureActions::start: 1282 ctx->bus->yield_method_call(ctx->yield, ec, systemDService, 1283 systemDObjPath, systemDMgrIntf, 1284 "StartUnit", serviceName, "replace"); 1285 break; 1286 case ipmi::SupportedFeatureActions::disable: 1287 if (disableOrEnableUnitFiles == true) 1288 { 1289 ctx->bus->yield_method_call( 1290 ctx->yield, ec, systemDService, systemDObjPath, 1291 systemDMgrIntf, "DisableUnitFiles", 1292 std::array<const char*, 1>{serviceName.c_str()}, 1293 runtimeOnly); 1294 } 1295 ctx->bus->yield_method_call( 1296 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf, 1297 "MaskUnitFiles", 1298 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly, 1299 force); 1300 break; 1301 case ipmi::SupportedFeatureActions::enable: 1302 ctx->bus->yield_method_call( 1303 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf, 1304 "UnmaskUnitFiles", 1305 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly); 1306 if (disableOrEnableUnitFiles == true) 1307 { 1308 ctx->bus->yield_method_call( 1309 ctx->yield, ec, systemDService, systemDObjPath, 1310 systemDMgrIntf, "EnableUnitFiles", 1311 std::array<const char*, 1>{serviceName.c_str()}, 1312 runtimeOnly, force); 1313 } 1314 break; 1315 default: 1316 phosphor::logging::log<phosphor::logging::level::WARNING>( 1317 "ERROR: Invalid feature action selected", 1318 phosphor::logging::entry("ACTION=%d", enable)); 1319 return ipmi::responseInvalidFieldRequest(); 1320 } 1321 if (ec) 1322 { 1323 phosphor::logging::log<phosphor::logging::level::WARNING>( 1324 "ERROR: Service start or stop failed", 1325 phosphor::logging::entry("SERVICE=%s", serviceName.c_str())); 1326 return ipmi::responseUnspecifiedError(); 1327 } 1328 return ipmi::responseSuccess(); 1329 } 1330 1331 static std::string getMCTPServiceName(const std::string& objectPath) 1332 { 1333 const auto serviceArgument = boost::algorithm::replace_all_copy( 1334 boost::algorithm::replace_first_copy( 1335 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""), 1336 "/", "_2f"); 1337 std::string unitName = 1338 "xyz.openbmc_project.mctpd@" + serviceArgument + ".service"; 1339 return unitName; 1340 } 1341 1342 static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx, 1343 const uint8_t enable, 1344 const std::string& binding) 1345 { 1346 std::vector<std::string> configPaths; 1347 try 1348 { 1349 configPaths = getMCTPServiceConfigPaths(ctx); 1350 } 1351 catch (const std::exception& e) 1352 { 1353 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 1354 return ipmi::responseUnspecifiedError(); 1355 } 1356 1357 for (const auto& objectPath : configPaths) 1358 { 1359 auto const pos = objectPath.find_last_of('/'); 1360 if (binding == objectPath.substr(pos + 1)) 1361 { 1362 return startOrStopService(ctx, enable, 1363 getMCTPServiceName(objectPath), false); 1364 } 1365 } 1366 return ipmi::responseSuccess(); 1367 } 1368 1369 /** @brief implements MTM BMC Feature Control IPMI command which can be 1370 * used to enable or disable the supported BMC features. 1371 * @param yield - context object that represents the currently executing 1372 * coroutine 1373 * @param feature - feature enum to enable or disable 1374 * @param enable - enable or disable the feature 1375 * @param featureArg - custom arguments for that feature 1376 * @param reserved - reserved for future use 1377 * 1378 * @returns IPMI completion code 1379 */ 1380 ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx, 1381 const uint8_t feature, 1382 const uint8_t enable, 1383 const uint8_t featureArg, 1384 const uint16_t reserved) 1385 { 1386 if (reserved != 0) 1387 { 1388 return ipmi::responseInvalidFieldRequest(); 1389 } 1390 1391 switch (feature) 1392 { 1393 case ipmi::SupportedFeatureControls::mctp: 1394 switch (featureArg) 1395 { 1396 case ipmi::SupportedMCTPBindings::mctpPCIe: 1397 return handleMCTPFeature(ctx, enable, "MCTP_PCIe"); 1398 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP: 1399 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP"); 1400 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot: 1401 return handleMCTPFeature(ctx, enable, 1402 "MCTP_SMBus_PCIe_slot"); 1403 default: 1404 return ipmi::responseInvalidFieldRequest(); 1405 } 1406 break; 1407 case ipmi::SupportedFeatureControls::pcieScan: 1408 if (featureArg != 0) 1409 { 1410 return ipmi::responseInvalidFieldRequest(); 1411 } 1412 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service"); 1413 break; 1414 default: 1415 return ipmi::responseInvalidFieldRequest(); 1416 } 1417 return ipmi::responseSuccess(); 1418 } 1419 } // namespace ipmi 1420 1421 void register_mtm_commands() __attribute__((constructor)); 1422 void register_mtm_commands() 1423 { 1424 // <Get SM Signal> 1425 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 1426 ipmi::intel::general::cmdGetSmSignal, 1427 ipmi::Privilege::Admin, ipmi::appMTMGetSignal); 1428 1429 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 1430 ipmi::intel::general::cmdSetSmSignal, 1431 ipmi::Privilege::Admin, ipmi::appMTMSetSignal); 1432 1433 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 1434 ipmi::intel::general::cmdMtmKeepAlive, 1435 ipmi::Privilege::Admin, ipmi::mtmKeepAlive); 1436 1437 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 1438 ipmi::intel::general::cmdSetManufacturingData, 1439 ipmi::Privilege::Admin, ipmi::setManufacturingData); 1440 1441 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 1442 ipmi::intel::general::cmdGetManufacturingData, 1443 ipmi::Privilege::Admin, ipmi::getManufacturingData); 1444 1445 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 1446 ipmi::intel::general::cmdSetFITcLayout, 1447 ipmi::Privilege::Admin, ipmi::setFITcLayout); 1448 1449 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 1450 ipmi::intel::general::cmdMTMBMCFeatureControl, 1451 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl); 1452 1453 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1454 ipmi::intel::general::cmdSlotI2CMasterWriteRead, 1455 ipmi::Privilege::Admin, 1456 ipmi::appSlotI2CMasterWriteRead); 1457 1458 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform, 1459 ipmi::intel::platform::cmdClearCMOS, 1460 ipmi::Privilege::Admin, ipmi::clearCMOS); 1461 1462 ipmi::registerFilter(ipmi::prioOemBase, 1463 [](ipmi::message::Request::ptr request) { 1464 return ipmi::mfgFilterMessage(request); 1465 }); 1466 } 1467