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