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