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