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