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