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/container/flat_map.hpp> 20 #include <filesystem> 21 #include <fstream> 22 #include <ipmid/api.hpp> 23 #include <manufacturingcommands.hpp> 24 #include <oemcommands.hpp> 25 26 namespace ipmi 27 { 28 29 Manufacturing mtm; 30 31 static auto revertTimeOut = 32 std::chrono::duration_cast<std::chrono::microseconds>( 33 std::chrono::seconds(60)); // 1 minute timeout 34 35 static constexpr uint8_t slotAddressTypeBus = 0; 36 static constexpr uint8_t slotAddressTypeUniqueid = 1; 37 static constexpr uint8_t slotI2CMaxReadSize = 35; 38 39 static constexpr const char* callbackMgrService = 40 "xyz.openbmc_project.CallbackManager"; 41 static constexpr const char* callbackMgrIntf = 42 "xyz.openbmc_project.CallbackManager"; 43 static constexpr const char* callbackMgrObjPath = 44 "/xyz/openbmc_project/CallbackManager"; 45 static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate"; 46 47 const static constexpr char* systemDService = "org.freedesktop.systemd1"; 48 const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1"; 49 const static constexpr char* systemDMgrIntf = 50 "org.freedesktop.systemd1.Manager"; 51 const static constexpr char* pidControlService = "phosphor-pid-control.service"; 52 53 static inline Cc resetMtmTimer(ipmi::Context::ptr ctx) 54 { 55 boost::system::error_code ec; 56 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService, 57 specialModeObjPath, specialModeIntf, 58 "ResetTimer"); 59 if (ec) 60 { 61 phosphor::logging::log<phosphor::logging::level::ERR>( 62 "Failed to reset the manufacturing mode timer"); 63 return ccUnspecifiedError; 64 } 65 return ccSuccess; 66 } 67 68 int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path) 69 { 70 switch (signal) 71 { 72 case SmSignalGet::smPowerButton: 73 path = "/xyz/openbmc_project/chassis/buttons/power"; 74 break; 75 case SmSignalGet::smResetButton: 76 path = "/xyz/openbmc_project/chassis/buttons/reset"; 77 break; 78 case SmSignalGet::smNMIButton: 79 path = "/xyz/openbmc_project/chassis/buttons/nmi"; 80 break; 81 case SmSignalGet::smIdentifyButton: 82 path = "/xyz/openbmc_project/chassis/buttons/id"; 83 break; 84 default: 85 return -1; 86 break; 87 } 88 return 0; 89 } 90 91 ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState) 92 { 93 LedProperty* ledProp = mtm.findLedProperty(signal); 94 if (ledProp == nullptr) 95 { 96 return IPMI_CC_INVALID_FIELD_REQUEST; 97 } 98 99 std::string ledName = ledProp->getName(); 100 std::string ledService = ledServicePrefix + ledName; 101 std::string ledPath = ledPathPrefix + ledName; 102 ipmi::Value presentState; 103 104 if (false == ledProp->getLock()) 105 { 106 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf, 107 "State", &presentState) != 0) 108 { 109 return IPMI_CC_UNSPECIFIED_ERROR; 110 } 111 ledProp->setPrevState(std::get<std::string>(presentState)); 112 ledProp->setLock(true); 113 if (signal == SmSignalSet::smPowerFaultLed || 114 signal == SmSignalSet::smSystemReadyLed) 115 { 116 mtm.revertLedCallback = true; 117 } 118 } 119 if (mtm.setProperty(ledService, ledPath, ledIntf, "State", 120 ledStateStr + setState) != 0) 121 { 122 return IPMI_CC_UNSPECIFIED_ERROR; 123 } 124 return IPMI_CC_OK; 125 } 126 127 ipmi_ret_t ledRevert(SmSignalSet signal) 128 { 129 LedProperty* ledProp = mtm.findLedProperty(signal); 130 if (ledProp == nullptr) 131 { 132 return IPMI_CC_INVALID_FIELD_REQUEST; 133 } 134 if (true == ledProp->getLock()) 135 { 136 ledProp->setLock(false); 137 if (signal == SmSignalSet::smPowerFaultLed || 138 signal == SmSignalSet::smSystemReadyLed) 139 { 140 try 141 { 142 ipmi::method_no_args::callDbusMethod( 143 *getSdBus(), callbackMgrService, callbackMgrObjPath, 144 callbackMgrIntf, retriggerLedUpdate); 145 } 146 catch (sdbusplus::exception_t& e) 147 { 148 return IPMI_CC_UNSPECIFIED_ERROR; 149 } 150 mtm.revertLedCallback = false; 151 } 152 else 153 { 154 std::string ledName = ledProp->getName(); 155 std::string ledService = ledServicePrefix + ledName; 156 std::string ledPath = ledPathPrefix + ledName; 157 if (mtm.setProperty(ledService, ledPath, ledIntf, "State", 158 ledProp->getPrevState()) != 0) 159 { 160 return IPMI_CC_UNSPECIFIED_ERROR; 161 } 162 } 163 } 164 return IPMI_CC_OK; 165 } 166 167 void Manufacturing::initData() 168 { 169 ledPropertyList.push_back( 170 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber")); 171 ledPropertyList.push_back( 172 LedProperty(SmSignalSet::smSystemReadyLed, "status_green")); 173 ledPropertyList.push_back( 174 LedProperty(SmSignalSet::smIdentifyLed, "identify")); 175 } 176 177 void Manufacturing::revertTimerHandler() 178 { 179 180 #ifdef BMC_VALIDATION_UNSECURE_FEATURE 181 if (mtm.getMfgMode() == SpecialMode::valUnsecure) 182 { 183 // Don't revert the behaviour for validation unsecure mode. 184 return; 185 } 186 #endif 187 if (revertFanPWM) 188 { 189 revertFanPWM = false; 190 disablePidControlService(false); 191 } 192 193 if (mtmTestBeepFd != -1) 194 { 195 ::close(mtmTestBeepFd); 196 mtmTestBeepFd = -1; 197 } 198 199 for (const auto& ledProperty : ledPropertyList) 200 { 201 const std::string& ledName = ledProperty.getName(); 202 ledRevert(ledProperty.getSignal()); 203 } 204 } 205 206 Manufacturing::Manufacturing() : 207 revertTimer([&](void) { revertTimerHandler(); }) 208 { 209 initData(); 210 } 211 212 int8_t Manufacturing::getProperty(const std::string& service, 213 const std::string& path, 214 const std::string& interface, 215 const std::string& propertyName, 216 ipmi::Value* reply) 217 { 218 try 219 { 220 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface, 221 propertyName); 222 } 223 catch (const sdbusplus::exception::SdBusError& e) 224 { 225 phosphor::logging::log<phosphor::logging::level::INFO>( 226 "ERROR: getProperty"); 227 return -1; 228 } 229 230 return 0; 231 } 232 233 int8_t Manufacturing::setProperty(const std::string& service, 234 const std::string& path, 235 const std::string& interface, 236 const std::string& propertyName, 237 ipmi::Value value) 238 { 239 try 240 { 241 ipmi::setDbusProperty(*getSdBus(), service, path, interface, 242 propertyName, value); 243 } 244 catch (const sdbusplus::exception::SdBusError& e) 245 { 246 phosphor::logging::log<phosphor::logging::level::INFO>( 247 "ERROR: setProperty"); 248 return -1; 249 } 250 251 return 0; 252 } 253 254 int8_t Manufacturing::disablePidControlService(const bool disable) 255 { 256 try 257 { 258 auto dbus = getSdBus(); 259 auto method = dbus->new_method_call(systemDService, systemDObjPath, 260 systemDMgrIntf, 261 disable ? "StopUnit" : "StartUnit"); 262 method.append(pidControlService, "replace"); 263 auto reply = dbus->call(method); 264 } 265 catch (const sdbusplus::exception::SdBusError& e) 266 { 267 phosphor::logging::log<phosphor::logging::level::INFO>( 268 "ERROR: phosphor-pid-control service start or stop failed"); 269 return -1; 270 } 271 return 0; 272 } 273 274 ipmi::RspType<uint8_t, // Signal value 275 std::optional<uint16_t> // Fan tach value 276 > 277 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte, 278 uint8_t instance, uint8_t actionByte) 279 { 280 // mfg filter logic is used to allow MTM get signal command only in 281 // manfacturing mode. 282 283 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte); 284 SmActionGet action = static_cast<SmActionGet>(actionByte); 285 286 switch (signalType) 287 { 288 case SmSignalGet::smChassisIntrusion: 289 { 290 ipmi::Value reply; 291 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf, 292 "Status", &reply) < 0) 293 { 294 return ipmi::responseInvalidFieldRequest(); 295 } 296 std::string* intrusionStatus = std::get_if<std::string>(&reply); 297 if (!intrusionStatus) 298 { 299 return ipmi::responseUnspecifiedError(); 300 } 301 302 uint8_t status = 0; 303 if (!intrusionStatus->compare("Normal")) 304 { 305 status = static_cast<uint8_t>(IntrusionStatus::normal); 306 } 307 else if (!intrusionStatus->compare("HardwareIntrusion")) 308 { 309 status = 310 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion); 311 } 312 else if (!intrusionStatus->compare("TamperingDetected")) 313 { 314 status = 315 static_cast<uint8_t>(IntrusionStatus::tamperingDetected); 316 } 317 else 318 { 319 return ipmi::responseUnspecifiedError(); 320 } 321 return ipmi::responseSuccess(status, std::nullopt); 322 } 323 case SmSignalGet::smFanPwmGet: 324 { 325 ipmi::Value reply; 326 std::string fullPath = fanPwmPath + std::to_string(instance + 1); 327 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value", 328 &reply) < 0) 329 { 330 return ipmi::responseInvalidFieldRequest(); 331 } 332 double* doubleVal = std::get_if<double>(&reply); 333 if (doubleVal == nullptr) 334 { 335 return ipmi::responseUnspecifiedError(); 336 } 337 uint8_t sensorVal = std::round(*doubleVal); 338 resetMtmTimer(ctx); 339 return ipmi::responseSuccess(sensorVal, std::nullopt); 340 } 341 break; 342 case SmSignalGet::smFanTachometerGet: 343 { 344 boost::system::error_code ec; 345 using objFlatMap = boost::container::flat_map< 346 std::string, boost::container::flat_map< 347 std::string, std::vector<std::string>>>; 348 349 auto flatMap = ctx->bus->yield_method_call<objFlatMap>( 350 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper", 351 "/xyz/openbmc_project/object_mapper", 352 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 353 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf}); 354 if (ec) 355 { 356 phosphor::logging::log<phosphor::logging::level::ERR>( 357 "Failed to query fan tach sub tree objects"); 358 return ipmi::responseUnspecifiedError(); 359 } 360 if (instance >= flatMap.size()) 361 { 362 return ipmi::responseInvalidFieldRequest(); 363 } 364 auto itr = flatMap.nth(instance); 365 ipmi::Value reply; 366 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value", 367 &reply) < 0) 368 { 369 return ipmi::responseInvalidFieldRequest(); 370 } 371 372 double* doubleVal = std::get_if<double>(&reply); 373 if (doubleVal == nullptr) 374 { 375 return ipmi::responseUnspecifiedError(); 376 } 377 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT; 378 std::optional<uint16_t> fanTach = std::round(*doubleVal); 379 380 resetMtmTimer(ctx); 381 return ipmi::responseSuccess(sensorVal, fanTach); 382 } 383 break; 384 case SmSignalGet::smIdentifyButton: 385 { 386 if (action == SmActionGet::revert || action == SmActionGet::ignore) 387 { 388 // ButtonMasked property is not supported for ID button as it is 389 // unnecessary. Hence if requested for revert / ignore, override 390 // it to sample action to make tools happy. 391 action = SmActionGet::sample; 392 } 393 // fall-through 394 } 395 case SmSignalGet::smResetButton: 396 case SmSignalGet::smPowerButton: 397 case SmSignalGet::smNMIButton: 398 { 399 std::string path; 400 if (getGpioPathForSmSignal(signalType, path) < 0) 401 { 402 return ipmi::responseInvalidFieldRequest(); 403 } 404 405 switch (action) 406 { 407 case SmActionGet::sample: 408 phosphor::logging::log<phosphor::logging::level::INFO>( 409 "case SmActionGet::sample"); 410 break; 411 case SmActionGet::ignore: 412 { 413 phosphor::logging::log<phosphor::logging::level::INFO>( 414 "case SmActionGet::ignore"); 415 if (mtm.setProperty(buttonService, path, buttonIntf, 416 "ButtonMasked", true) < 0) 417 { 418 return ipmi::responseUnspecifiedError(); 419 } 420 } 421 break; 422 case SmActionGet::revert: 423 { 424 phosphor::logging::log<phosphor::logging::level::INFO>( 425 "case SmActionGet::revert"); 426 if (mtm.setProperty(buttonService, path, buttonIntf, 427 "ButtonMasked", false) < 0) 428 { 429 return ipmi::responseUnspecifiedError(); 430 } 431 } 432 break; 433 434 default: 435 return ipmi::responseInvalidFieldRequest(); 436 break; 437 } 438 439 ipmi::Value reply; 440 if (mtm.getProperty(buttonService, path, buttonIntf, 441 "ButtonPressed", &reply) < 0) 442 { 443 return ipmi::responseUnspecifiedError(); 444 } 445 bool* valPtr = std::get_if<bool>(&reply); 446 if (valPtr == nullptr) 447 { 448 return ipmi::responseUnspecifiedError(); 449 } 450 resetMtmTimer(ctx); 451 uint8_t sensorVal = *valPtr; 452 return ipmi::responseSuccess(sensorVal, std::nullopt); 453 } 454 break; 455 case SmSignalGet::smNcsiDiag: 456 { 457 constexpr const char* netBasePath = "/sys/class/net/eth"; 458 constexpr const char* carrierSuffix = "/carrier"; 459 std::ifstream netIfs(netBasePath + std::to_string(instance) + 460 carrierSuffix); 461 if (!netIfs.good()) 462 { 463 return ipmi::responseInvalidFieldRequest(); 464 } 465 std::string carrier; 466 netIfs >> carrier; 467 resetMtmTimer(ctx); 468 return ipmi::responseSuccess( 469 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt); 470 } 471 break; 472 default: 473 return ipmi::responseInvalidFieldRequest(); 474 break; 475 } 476 } 477 478 ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte, 479 uint8_t instance, uint8_t actionByte, 480 std::optional<uint8_t> pwmSpeed) 481 { 482 // mfg filter logic is used to allow MTM set signal command only in 483 // manfacturing mode. 484 485 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte); 486 SmActionSet action = static_cast<SmActionSet>(actionByte); 487 Cc retCode = ccSuccess; 488 int8_t ret = 0; 489 490 switch (signalType) 491 { 492 case SmSignalSet::smPowerFaultLed: 493 case SmSignalSet::smSystemReadyLed: 494 case SmSignalSet::smIdentifyLed: 495 switch (action) 496 { 497 case SmActionSet::forceDeasserted: 498 { 499 phosphor::logging::log<phosphor::logging::level::INFO>( 500 "case SmActionSet::forceDeasserted"); 501 502 retCode = ledStoreAndSet(signalType, std::string("Off")); 503 if (retCode != ccSuccess) 504 { 505 return ipmi::response(retCode); 506 } 507 mtm.revertTimer.start(revertTimeOut); 508 } 509 break; 510 case SmActionSet::forceAsserted: 511 { 512 phosphor::logging::log<phosphor::logging::level::INFO>( 513 "case SmActionSet::forceAsserted"); 514 515 retCode = ledStoreAndSet(signalType, std::string("On")); 516 if (retCode != ccSuccess) 517 { 518 return ipmi::response(retCode); 519 } 520 mtm.revertTimer.start(revertTimeOut); 521 if (SmSignalSet::smPowerFaultLed == signalType) 522 { 523 // Deassert "system ready" 524 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed, 525 std::string("Off")); 526 } 527 else if (SmSignalSet::smSystemReadyLed == signalType) 528 { 529 // Deassert "fault led" 530 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed, 531 std::string("Off")); 532 } 533 } 534 break; 535 case SmActionSet::revert: 536 { 537 phosphor::logging::log<phosphor::logging::level::INFO>( 538 "case SmActionSet::revert"); 539 retCode = ledRevert(signalType); 540 } 541 break; 542 default: 543 { 544 return ipmi::responseInvalidFieldRequest(); 545 } 546 } 547 break; 548 case SmSignalSet::smFanPowerSpeed: 549 { 550 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed)) 551 { 552 return ipmi::responseReqDataLenInvalid(); 553 } 554 555 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100)) 556 { 557 return ipmi::responseInvalidFieldRequest(); 558 } 559 560 uint8_t pwmValue = 0; 561 switch (action) 562 { 563 case SmActionSet::revert: 564 { 565 if (mtm.revertFanPWM) 566 { 567 ret = mtm.disablePidControlService(false); 568 if (ret < 0) 569 { 570 return ipmi::responseUnspecifiedError(); 571 } 572 mtm.revertFanPWM = false; 573 } 574 } 575 break; 576 case SmActionSet::forceAsserted: 577 { 578 pwmValue = *pwmSpeed; 579 } // fall-through 580 case SmActionSet::forceDeasserted: 581 { 582 if (!mtm.revertFanPWM) 583 { 584 ret = mtm.disablePidControlService(true); 585 if (ret < 0) 586 { 587 return ipmi::responseUnspecifiedError(); 588 } 589 mtm.revertFanPWM = true; 590 } 591 mtm.revertTimer.start(revertTimeOut); 592 std::string fanPwmInstancePath = 593 fanPwmPath + std::to_string(instance + 1); 594 595 ret = 596 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf, 597 "Value", static_cast<double>(pwmValue)); 598 if (ret < 0) 599 { 600 return ipmi::responseUnspecifiedError(); 601 } 602 } 603 break; 604 default: 605 { 606 return ipmi::responseInvalidFieldRequest(); 607 } 608 } 609 } 610 break; 611 case SmSignalSet::smSpeaker: 612 { 613 phosphor::logging::log<phosphor::logging::level::INFO>( 614 "Performing Speaker SmActionSet", 615 phosphor::logging::entry("ACTION=%d", 616 static_cast<uint8_t>(action))); 617 switch (action) 618 { 619 case SmActionSet::forceAsserted: 620 { 621 char beepDevName[] = "/dev/input/event0"; 622 if (mtm.mtmTestBeepFd != -1) 623 { 624 phosphor::logging::log<phosphor::logging::level::INFO>( 625 "mtm beep device is opened already!"); 626 // returning success as already beep is in progress 627 return ipmi::response(retCode); 628 } 629 630 if ((mtm.mtmTestBeepFd = 631 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0) 632 { 633 phosphor::logging::log<phosphor::logging::level::ERR>( 634 "Failed to open input device"); 635 return ipmi::responseUnspecifiedError(); 636 } 637 638 struct input_event event; 639 event.type = EV_SND; 640 event.code = SND_TONE; 641 event.value = 2000; 642 643 if (::write(mtm.mtmTestBeepFd, &event, 644 sizeof(struct input_event)) != 645 sizeof(struct input_event)) 646 { 647 phosphor::logging::log<phosphor::logging::level::ERR>( 648 "Failed to write a tone sound event"); 649 ::close(mtm.mtmTestBeepFd); 650 mtm.mtmTestBeepFd = -1; 651 return ipmi::responseUnspecifiedError(); 652 } 653 mtm.revertTimer.start(revertTimeOut); 654 } 655 break; 656 case SmActionSet::revert: 657 case SmActionSet::forceDeasserted: 658 { 659 if (mtm.mtmTestBeepFd != -1) 660 { 661 ::close(mtm.mtmTestBeepFd); 662 mtm.mtmTestBeepFd = -1; 663 } 664 } 665 break; 666 default: 667 { 668 return ipmi::responseInvalidFieldRequest(); 669 } 670 } 671 } 672 break; 673 case SmSignalSet::smDiskFaultLed: 674 { 675 boost::system::error_code ec; 676 using objPaths = std::vector<std::string>; 677 std::string driveBasePath = 678 "/xyz/openbmc_project/inventory/item/drive/"; 679 static constexpr const char* driveLedIntf = 680 "xyz.openbmc_project.Led.Group"; 681 static constexpr const char* hsbpService = 682 "xyz.openbmc_project.HsbpManager"; 683 684 auto driveList = ctx->bus->yield_method_call<objPaths>( 685 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper", 686 "/xyz/openbmc_project/object_mapper", 687 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 688 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf}); 689 if (ec) 690 { 691 phosphor::logging::log<phosphor::logging::level::ERR>( 692 "Failed to query HSBP drive sub tree objects"); 693 return ipmi::responseUnspecifiedError(); 694 } 695 std::string driveObjPath = 696 driveBasePath + "Drive_" + std::to_string(instance + 1); 697 if (std::find(driveList.begin(), driveList.end(), driveObjPath) == 698 driveList.end()) 699 { 700 return ipmi::responseInvalidFieldRequest(); 701 } 702 bool driveLedState = false; 703 switch (action) 704 { 705 case SmActionSet::forceAsserted: 706 { 707 driveLedState = true; 708 } 709 break; 710 case SmActionSet::revert: 711 { 712 driveLedState = false; 713 } 714 break; 715 case SmActionSet::forceDeasserted: 716 { 717 driveLedState = false; 718 } 719 break; 720 default: 721 { 722 return ipmi::responseInvalidFieldRequest(); 723 } 724 } 725 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf, 726 "Asserted", driveLedState); 727 if (ret < 0) 728 { 729 return ipmi::responseUnspecifiedError(); 730 } 731 } 732 break; 733 default: 734 { 735 return ipmi::responseInvalidFieldRequest(); 736 } 737 } 738 if (retCode == ccSuccess) 739 { 740 resetMtmTimer(ctx); 741 } 742 return ipmi::response(retCode); 743 } 744 745 ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved, 746 const std::array<char, 5>& intentionalSignature) 747 { 748 // mfg filter logic is used to allow MTM keep alive command only in 749 // manfacturing mode 750 751 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'}; 752 if (intentionalSignature != signatureOk || reserved != 0) 753 { 754 return ipmi::responseInvalidFieldRequest(); 755 } 756 return ipmi::response(resetMtmTimer(ctx)); 757 } 758 759 static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd) 760 { 761 return (netFn << 8) | cmd; 762 } 763 764 ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request) 765 { 766 // Restricted commands, must be executed only in Manufacturing mode 767 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd)) 768 { 769 // i2c master write read command needs additional checking 770 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead): 771 if (request->payload.size() > 4) 772 { 773 // Allow write data count > 1 only in Special mode 774 if (mtm.getMfgMode() == SpecialMode::none) 775 { 776 return ipmi::ccInsufficientPrivilege; 777 } 778 } 779 return ipmi::ccSuccess; 780 case makeCmdKey(ipmi::netFnOemOne, 781 ipmi::intel::general::cmdGetSmSignal): 782 case makeCmdKey(ipmi::netFnOemOne, 783 ipmi::intel::general::cmdSetSmSignal): 784 case makeCmdKey(ipmi::netFnOemOne, 785 ipmi::intel::general::cmdMtmKeepAlive): 786 case makeCmdKey(ipmi::netFnOemOne, 787 ipmi::intel::general::cmdSetManufacturingData): 788 case makeCmdKey(ipmi::netFnOemOne, 789 ipmi::intel::general::cmdGetManufacturingData): 790 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData): 791 792 // Check for Special mode 793 if (mtm.getMfgMode() == SpecialMode::none) 794 { 795 return ipmi::ccInvalidCommand; 796 } 797 return ipmi::ccSuccess; 798 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry): 799 { 800 return ipmi::ccInvalidCommand; 801 } 802 } 803 return ipmi::ccSuccess; 804 } 805 806 static constexpr uint8_t maxEthSize = 6; 807 static constexpr uint8_t maxSupportedEth = 3; 808 static constexpr const char* factoryEthAddrBaseFileName = 809 "/var/sofs/factory-settings/network/mac/eth"; 810 811 ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType, 812 std::array<uint8_t, maxEthSize> ethData) 813 { 814 // mfg filter logic will restrict this command executing only in mfg mode. 815 if (dataType >= maxSupportedEth) 816 { 817 return ipmi::responseParmOutOfRange(); 818 } 819 820 constexpr uint8_t invalidData = 0; 821 constexpr uint8_t validData = 1; 822 constexpr uint8_t ethAddrStrSize = 823 19; // XX:XX:XX:XX:XX:XX + \n + null termination; 824 std::vector<uint8_t> buff(ethAddrStrSize); 825 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize, 826 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0), 827 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4), 828 ethData.at(5)); 829 std::ofstream oEthFile(factoryEthAddrBaseFileName + 830 std::to_string(dataType), 831 std::ofstream::out); 832 if (!oEthFile.good()) 833 { 834 return ipmi::responseUnspecifiedError(); 835 } 836 837 oEthFile << reinterpret_cast<char*>(buff.data()); 838 oEthFile << fflush; 839 oEthFile.close(); 840 841 resetMtmTimer(ctx); 842 return ipmi::responseSuccess(); 843 } 844 845 ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>> 846 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType) 847 { 848 // mfg filter logic will restrict this command executing only in mfg mode. 849 if (dataType >= maxSupportedEth) 850 { 851 return ipmi::responseParmOutOfRange(); 852 } 853 std::array<uint8_t, maxEthSize> ethData{0}; 854 constexpr uint8_t invalidData = 0; 855 constexpr uint8_t validData = 1; 856 857 std::ifstream iEthFile(factoryEthAddrBaseFileName + 858 std::to_string(dataType), 859 std::ifstream::in); 860 if (!iEthFile.good()) 861 { 862 return ipmi::responseSuccess(invalidData, ethData); 863 } 864 std::string ethStr; 865 iEthFile >> ethStr; 866 uint8_t* data = ethData.data(); 867 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", 868 data, (data + 1), (data + 2), (data + 3), (data + 4), 869 (data + 5)); 870 871 resetMtmTimer(ctx); 872 return ipmi::responseSuccess(validData, ethData); 873 } 874 875 /** @brief implements slot master write read IPMI command which can be used for 876 * low-level I2C/SMBus write, read or write-read access for PCIE slots 877 * @param reserved - skip 6 bit 878 * @param addressType - address type 879 * @param bbSlotNum - baseboard slot number 880 * @param riserSlotNum - riser slot number 881 * @param reserved2 - skip 2 bit 882 * @param slaveAddr - slave address 883 * @param readCount - number of bytes to be read 884 * @param writeData - data to be written 885 * 886 * @returns IPMI completion code plus response data 887 */ 888 ipmi::RspType<std::vector<uint8_t>> 889 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType, 890 uint3_t bbSlotNum, uint3_t riserSlotNum, 891 uint2_t resvered2, uint8_t slaveAddr, 892 uint8_t readCount, std::vector<uint8_t> writeData) 893 { 894 const size_t writeCount = writeData.size(); 895 std::string i2cBus; 896 if (addressType == slotAddressTypeBus) 897 { 898 std::string path = "/dev/i2c-mux/Riser_" + 899 std::to_string(static_cast<uint8_t>(bbSlotNum)) + 900 "_Mux/Pcie_Slot_" + 901 std::to_string(static_cast<uint8_t>(riserSlotNum)); 902 903 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path)) 904 { 905 i2cBus = std::filesystem::read_symlink(path); 906 } 907 else 908 { 909 phosphor::logging::log<phosphor::logging::level::ERR>( 910 "Master write read command: Cannot get BusID"); 911 return ipmi::responseInvalidFieldRequest(); 912 } 913 } 914 else if (addressType == slotAddressTypeUniqueid) 915 { 916 i2cBus = "/dev/i2c-" + 917 std::to_string(static_cast<uint8_t>(bbSlotNum) | 918 (static_cast<uint8_t>(riserSlotNum) << 3)); 919 } 920 else 921 { 922 phosphor::logging::log<phosphor::logging::level::ERR>( 923 "Master write read command: invalid request"); 924 return ipmi::responseInvalidFieldRequest(); 925 } 926 927 // Allow single byte write as it is offset byte to read the data, rest allow 928 // only in Special mode. 929 if (writeCount > 1) 930 { 931 if (mtm.getMfgMode() == SpecialMode::none) 932 { 933 return ipmi::responseInsufficientPrivilege(); 934 } 935 } 936 937 if (readCount > slotI2CMaxReadSize) 938 { 939 phosphor::logging::log<phosphor::logging::level::ERR>( 940 "Master write read command: Read count exceeds limit"); 941 return ipmi::responseParmOutOfRange(); 942 } 943 944 if (!readCount && !writeCount) 945 { 946 phosphor::logging::log<phosphor::logging::level::ERR>( 947 "Master write read command: Read & write count are 0"); 948 return ipmi::responseInvalidFieldRequest(); 949 } 950 951 std::vector<uint8_t> readBuf(readCount); 952 953 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf); 954 if (retI2C != ipmi::ccSuccess) 955 { 956 return ipmi::response(retI2C); 957 } 958 959 return ipmi::responseSuccess(readBuf); 960 } 961 962 ipmi::RspType<> clearCMOS() 963 { 964 // There is an i2c device on bus 4, the slave address is 0x70. Based on the 965 // spec, writing 0x1 to address 0x60 on this device, will trigger the clear 966 // CMOS action. 967 constexpr uint8_t slaveAddr = 0x70; 968 std::string i2cBus = "/dev/i2c-4"; 969 std::vector<uint8_t> writeData = {0x60, 0x1}; 970 std::vector<uint8_t> readBuf(0); 971 972 if (mtm.getMfgMode() == SpecialMode::none) 973 { 974 return ipmi::responseInsufficientPrivilege(); 975 } 976 977 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf); 978 return ipmi::response(retI2C); 979 } 980 } // namespace ipmi 981 982 void register_mtm_commands() __attribute__((constructor)); 983 void register_mtm_commands() 984 { 985 // <Get SM Signal> 986 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 987 ipmi::intel::general::cmdGetSmSignal, 988 ipmi::Privilege::Admin, ipmi::appMTMGetSignal); 989 990 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 991 ipmi::intel::general::cmdSetSmSignal, 992 ipmi::Privilege::Admin, ipmi::appMTMSetSignal); 993 994 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 995 ipmi::intel::general::cmdMtmKeepAlive, 996 ipmi::Privilege::Admin, ipmi::mtmKeepAlive); 997 998 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 999 ipmi::intel::general::cmdSetManufacturingData, 1000 ipmi::Privilege::Admin, ipmi::setManufacturingData); 1001 1002 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral, 1003 ipmi::intel::general::cmdGetManufacturingData, 1004 ipmi::Privilege::Admin, ipmi::getManufacturingData); 1005 1006 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1007 ipmi::intel::general::cmdSlotI2CMasterWriteRead, 1008 ipmi::Privilege::Admin, 1009 ipmi::appSlotI2CMasterWriteRead); 1010 1011 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform, 1012 ipmi::intel::platform::cmdClearCMOS, 1013 ipmi::Privilege::Admin, ipmi::clearCMOS); 1014 1015 ipmi::registerFilter(ipmi::prioOemBase, 1016 [](ipmi::message::Request::ptr request) { 1017 return ipmi::mfgFilterMessage(request); 1018 }); 1019 } 1020