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