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