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 "types.hpp" 18 #include "xyz/openbmc_project/Common/error.hpp" 19 #include "xyz/openbmc_project/Led/Physical/server.hpp" 20 21 #include <openssl/crypto.h> 22 #include <systemd/sd-journal.h> 23 24 #include <appcommands.hpp> 25 #include <boost/container/flat_map.hpp> 26 #include <boost/process/child.hpp> 27 #include <boost/process/io.hpp> 28 #include <com/intel/Control/OCOTShutdownPolicy/server.hpp> 29 #include <commandutils.hpp> 30 #include <gpiod.hpp> 31 #include <ipmid/api.hpp> 32 #include <ipmid/utils.hpp> 33 #include <nlohmann/json.hpp> 34 #include <oemcommands.hpp> 35 #include <phosphor-logging/log.hpp> 36 #include <sdbusplus/bus.hpp> 37 #include <sdbusplus/message/types.hpp> 38 #include <xyz/openbmc_project/Chassis/Control/NMISource/server.hpp> 39 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp> 40 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp> 41 #include <xyz/openbmc_project/Control/PowerSupplyRedundancy/server.hpp> 42 #include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp> 43 #include <xyz/openbmc_project/Control/Security/SpecialMode/server.hpp> 44 45 #include <array> 46 #include <filesystem> 47 #include <fstream> 48 #include <iostream> 49 #include <regex> 50 #include <set> 51 #include <string> 52 #include <variant> 53 #include <vector> 54 55 namespace ipmi 56 { 57 static void registerOEMFunctions() __attribute__((constructor)); 58 59 static constexpr size_t maxFRUStringLength = 0x3F; 60 61 static constexpr auto ethernetIntf = 62 "xyz.openbmc_project.Network.EthernetInterface"; 63 static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP"; 64 static constexpr auto networkService = "xyz.openbmc_project.Network"; 65 static constexpr auto networkRoot = "/xyz/openbmc_project/network"; 66 67 static constexpr const char* oemNmiSourceIntf = 68 "xyz.openbmc_project.Chassis.Control.NMISource"; 69 static constexpr const char* oemNmiSourceObjPath = 70 "/xyz/openbmc_project/Chassis/Control/NMISource"; 71 static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource"; 72 static constexpr const char* oemNmiEnabledObjPathProp = "Enabled"; 73 74 static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json"; 75 static constexpr const char* multiNodeObjPath = 76 "/xyz/openbmc_project/MultiNode/Status"; 77 static constexpr const char* multiNodeIntf = 78 "xyz.openbmc_project.Chassis.MultiNode"; 79 80 enum class NmiSource : uint8_t 81 { 82 none = 0, 83 frontPanelButton = 1, 84 watchdog = 2, 85 chassisCmd = 3, 86 memoryError = 4, 87 pciBusError = 5, 88 pch = 6, 89 chipset = 7, 90 }; 91 92 enum class SpecialUserIndex : uint8_t 93 { 94 rootUser = 0, 95 atScaleDebugUser = 1 96 }; 97 98 static constexpr const char* restricionModeService = 99 "xyz.openbmc_project.RestrictionMode.Manager"; 100 static constexpr const char* restricionModeBasePath = 101 "/xyz/openbmc_project/control/security/restriction_mode"; 102 static constexpr const char* restricionModeIntf = 103 "xyz.openbmc_project.Control.Security.RestrictionMode"; 104 static constexpr const char* restricionModeProperty = "RestrictionMode"; 105 106 static constexpr const char* specialModeService = 107 "xyz.openbmc_project.SpecialMode"; 108 static constexpr const char* specialModeBasePath = 109 "/xyz/openbmc_project/security/special_mode"; 110 static constexpr const char* specialModeIntf = 111 "xyz.openbmc_project.Security.SpecialMode"; 112 static constexpr const char* specialModeProperty = "SpecialMode"; 113 114 static constexpr const char* dBusPropertyIntf = 115 "org.freedesktop.DBus.Properties"; 116 static constexpr const char* dBusPropertyGetMethod = "Get"; 117 static constexpr const char* dBusPropertySetMethod = "Set"; 118 119 // return code: 0 successful 120 int8_t getChassisSerialNumber(sdbusplus::bus_t& bus, std::string& serial) 121 { 122 std::string objpath = "/xyz/openbmc_project/FruDevice"; 123 std::string intf = "xyz.openbmc_project.FruDeviceManager"; 124 std::string service = getService(bus, intf, objpath); 125 ObjectValueTree valueTree = getManagedObjects(bus, service, "/"); 126 if (valueTree.empty()) 127 { 128 phosphor::logging::log<phosphor::logging::level::ERR>( 129 "No object implements interface", 130 phosphor::logging::entry("INTF=%s", intf.c_str())); 131 return -1; 132 } 133 134 for (const auto& item : valueTree) 135 { 136 auto interface = item.second.find("xyz.openbmc_project.FruDevice"); 137 if (interface == item.second.end()) 138 { 139 continue; 140 } 141 142 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER"); 143 if (property == interface->second.end()) 144 { 145 continue; 146 } 147 148 try 149 { 150 Value variant = property->second; 151 std::string& result = std::get<std::string>(variant); 152 if (result.size() > maxFRUStringLength) 153 { 154 phosphor::logging::log<phosphor::logging::level::ERR>( 155 "FRU serial number exceed maximum length"); 156 return -1; 157 } 158 serial = result; 159 return 0; 160 } 161 catch (const std::bad_variant_access& e) 162 { 163 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 164 return -1; 165 } 166 } 167 return -1; 168 } 169 170 namespace mailbox 171 { 172 static uint8_t bus = 4; 173 static std::string i2cBus = "/dev/i2c-" + std::to_string(bus); 174 static uint8_t targetAddr = 56; 175 static constexpr auto systemRoot = "/xyz/openbmc_project/inventory/system"; 176 static constexpr auto sessionIntf = "xyz.openbmc_project.Configuration.PFR"; 177 const std::string match = "Baseboard/PFR"; 178 static bool i2cConfigLoaded = false; 179 // Command register for UFM provisioning/access commands; read/write allowed 180 // from CPU/BMC. 181 static const constexpr uint8_t provisioningCommand = 0x0b; 182 // Trigger register for the command set in the previous offset. 183 static const constexpr uint8_t triggerCommand = 0x0c; 184 // Set 0x0c to 0x05 to execute command specified at “UFM/Provisioning Command” 185 // register 186 static const constexpr uint8_t flushRead = 0x05; 187 // FIFO read registers 188 std::set<uint8_t> readFifoReg = {0x08, 0x0C, 0x0D, 0x13}; 189 190 // UFM Read FIFO 191 static const constexpr uint8_t readFifo = 0x0e; 192 193 enum registerType : uint8_t 194 { 195 singleByteRegister = 0, 196 fifoReadRegister, 197 198 }; 199 200 void loadPfrConfig(ipmi::Context::ptr& ctx, bool& i2cConfigLoaded) 201 { 202 ipmi::ObjectTree objectTree; 203 204 boost::system::error_code ec = ipmi::getAllDbusObjects( 205 ctx, systemRoot, sessionIntf, match, objectTree); 206 207 if (ec) 208 { 209 phosphor::logging::log<phosphor::logging::level::ERR>( 210 "Failed to fetch PFR object from dbus", 211 phosphor::logging::entry("INTERFACE=%s", sessionIntf), 212 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 213 214 return; 215 } 216 217 for (auto& softObject : objectTree) 218 { 219 const std::string& objPath = softObject.first; 220 const std::string& serviceName = softObject.second.begin()->first; 221 // PFR object found.. check for PFR support 222 ipmi::PropertyMap result; 223 224 ec = ipmi::getAllDbusProperties(ctx, serviceName, objPath, sessionIntf, 225 result); 226 227 if (ec) 228 { 229 phosphor::logging::log<phosphor::logging::level::ERR>( 230 "Failed to fetch pfr properties", 231 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 232 return; 233 } 234 235 const uint64_t* i2cBusNum = nullptr; 236 const uint64_t* address = nullptr; 237 238 for (const auto& [propName, propVariant] : result) 239 { 240 if (propName == "Address") 241 { 242 address = std::get_if<uint64_t>(&propVariant); 243 } 244 else if (propName == "Bus") 245 { 246 i2cBusNum = std::get_if<uint64_t>(&propVariant); 247 } 248 } 249 250 if ((address == nullptr) || (i2cBusNum == nullptr)) 251 { 252 phosphor::logging::log<phosphor::logging::level::ERR>( 253 "Unable to read the pfr properties"); 254 return; 255 } 256 257 bus = static_cast<int>(*i2cBusNum); 258 i2cBus = "/dev/i2c-" + std::to_string(bus); 259 targetAddr = static_cast<int>(*address); 260 261 i2cConfigLoaded = true; 262 } 263 } 264 265 void writefifo(const uint8_t cmdReg, const uint8_t val) 266 { 267 // Based on the spec, writing cmdReg to address val on this device, will 268 // trigger the write FIFO operation. 269 std::vector<uint8_t> writeData = {cmdReg, val}; 270 std::vector<uint8_t> readBuf(0); 271 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, targetAddr, writeData, 272 readBuf); 273 } 274 275 } // namespace mailbox 276 277 ipmi::RspType<std::string> ipmiOEMGetBmcVersionString() 278 { 279 static std::string version{}; 280 if (version.empty()) 281 { 282 std::regex expr{"^VERSION_ID=(.*)$"}; 283 static constexpr auto osReleasePath{"/etc/os-release"}; 284 std::ifstream ifs(osReleasePath); 285 if (!ifs.is_open()) 286 { 287 version = "os-release not present"; 288 } 289 std::string line{}; 290 while (std::getline(ifs, line)) 291 { 292 std::smatch sm; 293 if (regex_match(line, sm, expr)) 294 { 295 if (sm.size() == 2) 296 { 297 std::string v = sm[1].str(); 298 // remove the quotes 299 v.erase(std::remove(v.begin(), v.end(), '\"'), v.end()); 300 version = v; 301 break; 302 } 303 } 304 } 305 ifs.close(); 306 if (version.empty()) 307 { 308 version = "VERSION_ID not present"; 309 } 310 } 311 return ipmi::responseSuccess(version); 312 } 313 314 // Returns the Chassis Identifier (serial #) 315 ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 316 ipmi_request_t request, 317 ipmi_response_t response, 318 ipmi_data_len_t dataLen, 319 ipmi_context_t context) 320 { 321 std::string serial; 322 if (*dataLen != 0) // invalid request if there are extra parameters 323 { 324 *dataLen = 0; 325 return IPMI_CC_REQ_DATA_LEN_INVALID; 326 } 327 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 328 if (getChassisSerialNumber(*dbus, serial) == 0) 329 { 330 *dataLen = serial.size(); // length will never exceed response length 331 // as it is checked in getChassisSerialNumber 332 char* resp = static_cast<char*>(response); 333 serial.copy(resp, *dataLen); 334 return IPMI_CC_OK; 335 } 336 *dataLen = 0; 337 return IPMI_CC_RESPONSE_ERROR; 338 } 339 340 ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 341 ipmi_request_t request, 342 ipmi_response_t response, 343 ipmi_data_len_t dataLen, ipmi_context_t context) 344 { 345 static constexpr size_t safeBufferLength = 50; 346 char buf[safeBufferLength] = {0}; 347 GUIDData* Data = reinterpret_cast<GUIDData*>(request); 348 349 if (*dataLen != sizeof(GUIDData)) // 16bytes 350 { 351 *dataLen = 0; 352 return IPMI_CC_REQ_DATA_LEN_INVALID; 353 } 354 355 *dataLen = 0; 356 357 snprintf( 358 buf, safeBufferLength, 359 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 360 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1, 361 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1, 362 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4, 363 Data->node3, Data->node2, Data->node1); 364 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 365 std::string guid = buf; 366 367 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID"; 368 std::string intf = "xyz.openbmc_project.Common.UUID"; 369 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 370 std::string service = getService(*dbus, intf, objpath); 371 setDbusProperty(*dbus, service, objpath, intf, "UUID", guid); 372 return IPMI_CC_OK; 373 } 374 375 ipmi::RspType<> ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI, 376 uint7_t reserved1) 377 { 378 if (reserved1) 379 { 380 return ipmi::responseInvalidFieldRequest(); 381 } 382 383 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 384 385 try 386 { 387 auto service = ipmi::getService(*busp, bmcResetDisablesIntf, 388 bmcResetDisablesPath); 389 ipmi::setDbusProperty(*busp, service, bmcResetDisablesPath, 390 bmcResetDisablesIntf, "ResetOnSMI", 391 !disableResetOnSMI); 392 } 393 catch (const std::exception& e) 394 { 395 phosphor::logging::log<phosphor::logging::level::ERR>( 396 "Failed to set BMC reset disables", 397 phosphor::logging::entry("EXCEPTION=%s", e.what())); 398 return ipmi::responseUnspecifiedError(); 399 } 400 401 return ipmi::responseSuccess(); 402 } 403 404 ipmi::RspType<bool, // disableResetOnSMI 405 uint7_t // reserved 406 > 407 ipmiOEMGetBMCResetDisables() 408 { 409 bool disableResetOnSMI = true; 410 411 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 412 try 413 { 414 auto service = ipmi::getService(*busp, bmcResetDisablesIntf, 415 bmcResetDisablesPath); 416 Value variant = 417 ipmi::getDbusProperty(*busp, service, bmcResetDisablesPath, 418 bmcResetDisablesIntf, "ResetOnSMI"); 419 disableResetOnSMI = !std::get<bool>(variant); 420 } 421 catch (const std::exception& e) 422 { 423 phosphor::logging::log<phosphor::logging::level::ERR>( 424 "Failed to get BMC reset disables", 425 phosphor::logging::entry("EXCEPTION=%s", e.what())); 426 return ipmi::responseUnspecifiedError(); 427 } 428 429 return ipmi::responseSuccess(disableResetOnSMI, 0); 430 } 431 432 ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 433 ipmi_request_t request, ipmi_response_t response, 434 ipmi_data_len_t dataLen, ipmi_context_t context) 435 { 436 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request); 437 438 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength))) 439 { 440 *dataLen = 0; 441 return IPMI_CC_REQ_DATA_LEN_INVALID; 442 } 443 std::string idString((char*)data->biosId, data->biosIDLength); 444 for (auto idChar : idString) 445 { 446 if (!std::isprint(static_cast<unsigned char>(idChar))) 447 { 448 phosphor::logging::log<phosphor::logging::level::ERR>( 449 "BIOS ID contains non printable character"); 450 return IPMI_CC_INVALID_FIELD_REQUEST; 451 } 452 } 453 454 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 455 std::string service = getService(*dbus, biosVersionIntf, biosActiveObjPath); 456 setDbusProperty(*dbus, service, biosActiveObjPath, biosVersionIntf, 457 biosVersionProp, idString); 458 uint8_t* bytesWritten = static_cast<uint8_t*>(response); 459 *bytesWritten = 460 data->biosIDLength; // how many bytes are written into storage 461 *dataLen = 1; 462 return IPMI_CC_OK; 463 } 464 465 bool getActiveHSCSoftwareVersionInfo(std::string& hscVersion, size_t hscNumber) 466 { 467 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 468 try 469 { 470 std::string hsbpObjPath = "/xyz/openbmc_project/software/HSBP_" + 471 std::to_string(hscNumber); 472 auto service = getService(*dbus, biosVersionIntf, hsbpObjPath); 473 Value hscVersionValue = 474 getDbusProperty(*dbus, "xyz.openbmc_project.HsbpManager", 475 hsbpObjPath, biosVersionIntf, "Version"); 476 hscVersion = std::get<std::string>(hscVersionValue); 477 } 478 catch (const sdbusplus::exception_t& e) 479 { 480 phosphor::logging::log<phosphor::logging::level::INFO>( 481 "Failed to retrieve HSBP version information", 482 phosphor::logging::entry("HSBP Number=%d", hscNumber)); 483 return false; 484 } 485 return true; 486 } 487 488 bool getHscVerInfo(ipmi::Context::ptr ctx, uint8_t& hsc0Major, 489 uint8_t& hsc0Minor, uint8_t& hsc1Major, uint8_t& hsc1Minor, 490 uint8_t& hsc2Major, uint8_t& hsc2Minor) 491 { 492 std::string hscVersion; 493 std::array<uint8_t, 6> hscVersions{0}; 494 495 for (size_t hscNumber = 1; hscNumber <= 3; hscNumber++) 496 { 497 if (!getActiveHSCSoftwareVersionInfo(hscVersion, hscNumber)) 498 { 499 continue; 500 } 501 std::regex pattern1("(\\d+?).(\\d+?).(\\d+?)"); 502 constexpr size_t matchedPhosphor = 4; 503 std::smatch results; 504 // hscVersion = BOOT_VER.FPGA_VER.SECURITY_REVISION (Example: 00.02.01) 505 if (std::regex_match(hscVersion, results, pattern1)) 506 { 507 // Major version is FPGA_VER and Minor version is SECURITY_REV 508 if (results.size() == matchedPhosphor) 509 { 510 int index = (hscNumber - 1) * 2; 511 hscVersions[index] = 512 static_cast<uint8_t>(std::stoi(results[2])); 513 hscVersions[index + 1] = 514 static_cast<uint8_t>(std::stoi(results[3])); 515 } 516 } 517 } 518 hsc0Major = hscVersions[0]; 519 hsc0Minor = hscVersions[1]; 520 hsc1Major = hscVersions[2]; 521 hsc1Minor = hscVersions[3]; 522 hsc2Major = hscVersions[4]; 523 hsc2Minor = hscVersions[5]; 524 return true; 525 } 526 527 bool getSwVerInfo(ipmi::Context::ptr ctx, uint8_t& bmcMajor, uint8_t& bmcMinor, 528 uint8_t& meMajor, uint8_t& meMinor) 529 { 530 // step 1 : get BMC Major and Minor numbers from its DBUS property 531 std::string bmcVersion; 532 if (getActiveSoftwareVersionInfo(ctx, versionPurposeBMC, bmcVersion)) 533 { 534 return false; 535 } 536 537 std::optional<MetaRevision> rev = convertIntelVersion(bmcVersion); 538 if (rev.has_value()) 539 { 540 MetaRevision revision = rev.value(); 541 bmcMajor = revision.major; 542 543 revision.minor = (revision.minor > 99 ? 99 : revision.minor); 544 bmcMinor = revision.minor % 10 + (revision.minor / 10) * 16; 545 } 546 547 // step 2 : get ME Major and Minor numbers from its DBUS property 548 std::string meVersion; 549 if (getActiveSoftwareVersionInfo(ctx, versionPurposeME, meVersion)) 550 { 551 return false; 552 } 553 std::regex pattern1("(\\d+?).(\\d+?).(\\d+?).(\\d+?).(\\d+?)"); 554 constexpr size_t matchedPhosphor = 6; 555 std::smatch results; 556 if (std::regex_match(meVersion, results, pattern1)) 557 { 558 if (results.size() == matchedPhosphor) 559 { 560 meMajor = static_cast<uint8_t>(std::stoi(results[1])); 561 meMinor = static_cast<uint8_t>(std::stoi(results[2]) << 4 | 562 std::stoi(results[3])); 563 } 564 } 565 return true; 566 } 567 568 ipmi::RspType< 569 std::variant<std::string, 570 std::tuple<uint8_t, std::array<uint8_t, 2>, 571 std::array<uint8_t, 2>, std::array<uint8_t, 2>, 572 std::array<uint8_t, 2>, std::array<uint8_t, 2>>, 573 std::tuple<uint8_t, std::array<uint8_t, 2>>>> 574 ipmiOEMGetDeviceInfo(ipmi::Context::ptr ctx, uint8_t entityType, 575 std::optional<uint8_t> countToRead, 576 std::optional<uint8_t> offset) 577 { 578 if (entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer)) 579 { 580 return ipmi::responseInvalidFieldRequest(); 581 } 582 583 // handle OEM command items 584 switch (OEMDevEntityType(entityType)) 585 { 586 case OEMDevEntityType::biosId: 587 { 588 // Byte 2&3, Only used with selecting BIOS 589 if (!countToRead || !offset) 590 { 591 return ipmi::responseReqDataLenInvalid(); 592 } 593 594 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 595 std::string service = getService(*dbus, biosVersionIntf, 596 biosActiveObjPath); 597 try 598 { 599 Value variant = 600 getDbusProperty(*dbus, service, biosActiveObjPath, 601 biosVersionIntf, biosVersionProp); 602 std::string& idString = std::get<std::string>(variant); 603 if (*offset >= idString.size()) 604 { 605 return ipmi::responseParmOutOfRange(); 606 } 607 size_t length = 0; 608 if (*countToRead > (idString.size() - *offset)) 609 { 610 length = idString.size() - *offset; 611 } 612 else 613 { 614 length = *countToRead; 615 } 616 617 std::string readBuf = {0}; 618 readBuf.resize(length); 619 std::copy_n(idString.begin() + *offset, length, 620 (readBuf.begin())); 621 return ipmi::responseSuccess(readBuf); 622 } 623 catch (const std::bad_variant_access& e) 624 { 625 return ipmi::responseUnspecifiedError(); 626 } 627 } 628 break; 629 630 case OEMDevEntityType::devVer: 631 { 632 // Byte 2&3, Only used with selecting BIOS 633 if (countToRead || offset) 634 { 635 return ipmi::responseReqDataLenInvalid(); 636 } 637 638 constexpr const size_t verLen = 2; 639 constexpr const size_t verTotalLen = 10; 640 std::array<uint8_t, verLen> bmcBuf = {0xff, 0xff}; 641 std::array<uint8_t, verLen> hsc0Buf = {0xff, 0xff}; 642 std::array<uint8_t, verLen> hsc1Buf = {0xff, 0xff}; 643 std::array<uint8_t, verLen> meBuf = {0xff, 0xff}; 644 std::array<uint8_t, verLen> hsc2Buf = {0xff, 0xff}; 645 // data0/1: BMC version number; data6/7: ME version number 646 if (!getSwVerInfo(ctx, bmcBuf[0], bmcBuf[1], meBuf[0], meBuf[1])) 647 { 648 return ipmi::responseUnspecifiedError(); 649 } 650 if (!getHscVerInfo(ctx, hsc0Buf[0], hsc0Buf[1], hsc1Buf[0], 651 hsc1Buf[1], hsc2Buf[0], hsc2Buf[1])) 652 { 653 return ipmi::responseUnspecifiedError(); 654 } 655 return ipmi::responseSuccess( 656 std::tuple< 657 uint8_t, std::array<uint8_t, verLen>, 658 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>, 659 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>>{ 660 verTotalLen, bmcBuf, hsc0Buf, hsc1Buf, meBuf, hsc2Buf}); 661 } 662 break; 663 664 case OEMDevEntityType::sdrVer: 665 { 666 // Byte 2&3, Only used with selecting BIOS 667 if (countToRead || offset) 668 { 669 return ipmi::responseReqDataLenInvalid(); 670 } 671 672 constexpr const size_t sdrLen = 2; 673 std::array<uint8_t, sdrLen> readBuf = {0x01, 0x0}; 674 return ipmi::responseSuccess( 675 std::tuple<uint8_t, std::array<uint8_t, sdrLen>>{sdrLen, 676 readBuf}); 677 } 678 break; 679 680 default: 681 return ipmi::responseInvalidFieldRequest(); 682 } 683 } 684 685 ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 686 ipmi_request_t request, ipmi_response_t response, 687 ipmi_data_len_t dataLen, ipmi_context_t context) 688 { 689 if (*dataLen != 0) 690 { 691 *dataLen = 0; 692 return IPMI_CC_REQ_DATA_LEN_INVALID; 693 } 694 695 *dataLen = 1; 696 uint8_t* res = reinterpret_cast<uint8_t*>(response); 697 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no 698 // AIC is available so that BIOS will not timeout repeatly which leads to 699 // slow booting. 700 *res = 0; // Byte1=Count of SlotPosition/FruID records. 701 return IPMI_CC_OK; 702 } 703 704 ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 705 ipmi_request_t request, 706 ipmi_response_t response, 707 ipmi_data_len_t dataLen, 708 ipmi_context_t context) 709 { 710 GetPowerRestoreDelayRes* resp = 711 reinterpret_cast<GetPowerRestoreDelayRes*>(response); 712 713 if (*dataLen != 0) 714 { 715 *dataLen = 0; 716 return IPMI_CC_REQ_DATA_LEN_INVALID; 717 } 718 719 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 720 std::string service = getService(*dbus, powerRestoreDelayIntf, 721 powerRestoreDelayObjPath); 722 Value variant = getDbusProperty(*dbus, service, powerRestoreDelayObjPath, 723 powerRestoreDelayIntf, 724 powerRestoreDelayProp); 725 726 uint64_t val = std::get<uint64_t>(variant); 727 val /= 1000000UL; 728 uint16_t delay = val; 729 resp->byteLSB = delay; 730 resp->byteMSB = delay >> 8; 731 732 *dataLen = sizeof(GetPowerRestoreDelayRes); 733 734 return IPMI_CC_OK; 735 } 736 737 static uint8_t bcdToDec(uint8_t val) 738 { 739 return ((val / 16 * 10) + (val % 16)); 740 } 741 742 // Allows an update utility or system BIOS to send the status of an embedded 743 // firmware update attempt to the BMC. After received, BMC will create a logging 744 // record. 745 ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target, 746 uint8_t majorRevision, 747 uint8_t minorRevision, 748 uint32_t auxInfo) 749 { 750 std::string firmware; 751 int instance = (target & targetInstanceMask) >> targetInstanceShift; 752 target = (target & selEvtTargetMask) >> selEvtTargetShift; 753 754 /* make sure the status is 0, 1, or 2 as per the spec */ 755 if (status > 2) 756 { 757 return ipmi::response(ipmi::ccInvalidFieldRequest); 758 } 759 /* make sure the target is 0, 1, 2, or 4 as per the spec */ 760 if (target > 4 || target == 3) 761 { 762 return ipmi::response(ipmi::ccInvalidFieldRequest); 763 } 764 /*orignal OEM command is to record OEM SEL. 765 But openbmc does not support OEM SEL, so we redirect it to redfish event 766 logging. */ 767 std::string buildInfo; 768 std::string action; 769 switch (FWUpdateTarget(target)) 770 { 771 case FWUpdateTarget::targetBMC: 772 firmware = "BMC"; 773 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " + 774 std::to_string(bcdToDec(minorRevision)) + // BCD encoded 775 " BuildID: " + std::to_string(auxInfo); 776 buildInfo += std::to_string(auxInfo); 777 break; 778 case FWUpdateTarget::targetBIOS: 779 firmware = "BIOS"; 780 buildInfo = 781 "major: " + 782 std::to_string(bcdToDec(majorRevision)) + // BCD encoded 783 " minor: " + 784 std::to_string(bcdToDec(minorRevision)) + // BCD encoded 785 " ReleaseNumber: " + // ASCII encoded 786 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') + 787 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') + 788 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') + 789 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0'); 790 break; 791 case FWUpdateTarget::targetME: 792 firmware = "ME"; 793 buildInfo = 794 "major: " + std::to_string(majorRevision) + " minor1: " + 795 std::to_string(bcdToDec(minorRevision)) + // BCD encoded 796 " minor2: " + 797 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) + 798 " build1: " + 799 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) + 800 " build2: " + 801 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16))); 802 break; 803 case FWUpdateTarget::targetOEMEWS: 804 firmware = "EWS"; 805 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " + 806 std::to_string(bcdToDec(minorRevision)) + // BCD encoded 807 " BuildID: " + std::to_string(auxInfo); 808 break; 809 } 810 811 static const std::string openBMCMessageRegistryVersion("0.1"); 812 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion; 813 814 switch (status) 815 { 816 case 0x0: 817 action = "update started"; 818 redfishMsgID += ".FirmwareUpdateStarted"; 819 break; 820 case 0x1: 821 action = "update completed successfully"; 822 redfishMsgID += ".FirmwareUpdateCompleted"; 823 break; 824 case 0x2: 825 action = "update failure"; 826 redfishMsgID += ".FirmwareUpdateFailed"; 827 break; 828 default: 829 action = "unknown"; 830 break; 831 } 832 833 std::string firmwareInstanceStr = firmware + 834 " instance: " + std::to_string(instance); 835 std::string message("[firmware update] " + firmwareInstanceStr + 836 " status: <" + action + "> " + buildInfo); 837 838 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO, 839 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(), 840 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(), 841 buildInfo.c_str(), NULL); 842 return ipmi::responseSuccess(); 843 } 844 845 ipmi::RspType<uint8_t, std::vector<uint8_t>> 846 ipmiOEMSlotIpmb(ipmi::Context::ptr ctx, uint6_t reserved1, 847 uint2_t slotNumber, uint3_t baseBoardSlotNum, 848 uint3_t riserSlotNum, uint2_t reserved2, uint8_t targetAddr, 849 uint8_t netFn, uint8_t cmd, 850 std::optional<std::vector<uint8_t>> writeData) 851 { 852 if (reserved1 || reserved2) 853 { 854 return ipmi::responseInvalidFieldRequest(); 855 } 856 857 boost::system::error_code ec; 858 using ipmbResponse = std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, 859 std::vector<uint8_t>>; 860 ipmbResponse res = ctx->bus->yield_method_call<ipmbResponse>( 861 ctx->yield, ec, "xyz.openbmc_project.Ipmi.Channel.Ipmb", 862 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb", 863 "SlotIpmbRequest", static_cast<uint8_t>(slotNumber), 864 static_cast<uint8_t>(baseBoardSlotNum), targetAddr, netFn, cmd, 865 *writeData); 866 if (ec) 867 { 868 phosphor::logging::log<phosphor::logging::level::ERR>( 869 "Failed to call dbus method SlotIpmbRequest"); 870 return ipmi::responseUnspecifiedError(); 871 } 872 873 std::vector<uint8_t> dataReceived(0); 874 int status = -1; 875 uint8_t resNetFn = 0, resLun = 0, resCmd = 0, cc = 0; 876 877 std::tie(status, resNetFn, resLun, resCmd, cc, dataReceived) = res; 878 879 if (status) 880 { 881 phosphor::logging::log<phosphor::logging::level::ERR>( 882 "Failed to get response from SlotIpmbRequest"); 883 return ipmi::responseResponseError(); 884 } 885 return ipmi::responseSuccess(cc, dataReceived); 886 } 887 888 ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 889 ipmi_request_t request, 890 ipmi_response_t response, 891 ipmi_data_len_t dataLen, 892 ipmi_context_t context) 893 { 894 SetPowerRestoreDelayReq* data = 895 reinterpret_cast<SetPowerRestoreDelayReq*>(request); 896 uint16_t delay = 0; 897 898 if (*dataLen != sizeof(SetPowerRestoreDelayReq)) 899 { 900 *dataLen = 0; 901 return IPMI_CC_REQ_DATA_LEN_INVALID; 902 } 903 delay = data->byteMSB; 904 delay = (delay << 8) | data->byteLSB; 905 uint64_t val = delay * 1000000; 906 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 907 std::string service = getService(*dbus, powerRestoreDelayIntf, 908 powerRestoreDelayObjPath); 909 setDbusProperty(*dbus, service, powerRestoreDelayObjPath, 910 powerRestoreDelayIntf, powerRestoreDelayProp, val); 911 *dataLen = 0; 912 913 return IPMI_CC_OK; 914 } 915 916 static bool cpuPresent(const std::string& cpuName) 917 { 918 static constexpr const char* cpuPresencePathPrefix = 919 "/xyz/openbmc_project/inventory/system/chassis/motherboard/"; 920 static constexpr const char* cpuPresenceIntf = 921 "xyz.openbmc_project.Inventory.Item"; 922 std::string cpuPresencePath = cpuPresencePathPrefix + cpuName; 923 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 924 try 925 { 926 auto service = ipmi::getService(*busp, cpuPresenceIntf, 927 cpuPresencePath); 928 929 ipmi::Value result = ipmi::getDbusProperty( 930 *busp, service, cpuPresencePath, cpuPresenceIntf, "Present"); 931 return std::get<bool>(result); 932 } 933 catch (const std::exception& e) 934 { 935 phosphor::logging::log<phosphor::logging::level::INFO>( 936 "Cannot find processor presence", 937 phosphor::logging::entry("NAME=%s", cpuName.c_str())); 938 return false; 939 } 940 } 941 942 ipmi::RspType<bool, // CATERR Reset Enabled 943 bool, // ERR2 Reset Enabled 944 uint6_t, // reserved 945 uint8_t, // reserved, returns 0x3F 946 uint6_t, // CPU1 CATERR Count 947 uint2_t, // CPU1 Status 948 uint6_t, // CPU2 CATERR Count 949 uint2_t, // CPU2 Status 950 uint6_t, // CPU3 CATERR Count 951 uint2_t, // CPU3 Status 952 uint6_t, // CPU4 CATERR Count 953 uint2_t, // CPU4 Status 954 uint8_t // Crashdump Count 955 > 956 ipmiOEMGetProcessorErrConfig() 957 { 958 bool resetOnCATERR = false; 959 bool resetOnERR2 = false; 960 uint6_t cpu1CATERRCount = 0; 961 uint6_t cpu2CATERRCount = 0; 962 uint6_t cpu3CATERRCount = 0; 963 uint6_t cpu4CATERRCount = 0; 964 uint8_t crashdumpCount = 0; 965 uint2_t cpu1Status = cpuPresent("CPU_1") 966 ? types::enum_cast<uint8_t>(CPUStatus::enabled) 967 : types::enum_cast<uint8_t>(CPUStatus::notPresent); 968 uint2_t cpu2Status = cpuPresent("CPU_2") 969 ? types::enum_cast<uint8_t>(CPUStatus::enabled) 970 : types::enum_cast<uint8_t>(CPUStatus::notPresent); 971 uint2_t cpu3Status = cpuPresent("CPU_3") 972 ? types::enum_cast<uint8_t>(CPUStatus::enabled) 973 : types::enum_cast<uint8_t>(CPUStatus::notPresent); 974 uint2_t cpu4Status = cpuPresent("CPU_4") 975 ? types::enum_cast<uint8_t>(CPUStatus::enabled) 976 : types::enum_cast<uint8_t>(CPUStatus::notPresent); 977 978 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 979 try 980 { 981 auto service = ipmi::getService(*busp, processorErrConfigIntf, 982 processorErrConfigObjPath); 983 984 ipmi::PropertyMap result = ipmi::getAllDbusProperties( 985 *busp, service, processorErrConfigObjPath, processorErrConfigIntf); 986 resetOnCATERR = std::get<bool>(result.at("ResetOnCATERR")); 987 resetOnERR2 = std::get<bool>(result.at("ResetOnERR2")); 988 cpu1CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1")); 989 cpu2CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2")); 990 cpu3CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3")); 991 cpu4CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4")); 992 crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount")); 993 } 994 catch (const std::exception& e) 995 { 996 phosphor::logging::log<phosphor::logging::level::ERR>( 997 "Failed to fetch processor error config", 998 phosphor::logging::entry("ERROR=%s", e.what())); 999 return ipmi::responseUnspecifiedError(); 1000 } 1001 1002 return ipmi::responseSuccess(resetOnCATERR, resetOnERR2, 0, 0x3F, 1003 cpu1CATERRCount, cpu1Status, cpu2CATERRCount, 1004 cpu2Status, cpu3CATERRCount, cpu3Status, 1005 cpu4CATERRCount, cpu4Status, crashdumpCount); 1006 } 1007 1008 ipmi::RspType<> ipmiOEMSetProcessorErrConfig( 1009 bool resetOnCATERR, bool resetOnERR2, uint6_t reserved1, uint8_t reserved2, 1010 std::optional<bool> clearCPUErrorCount, 1011 std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3) 1012 { 1013 if (reserved1 || reserved2) 1014 { 1015 return ipmi::responseInvalidFieldRequest(); 1016 } 1017 1018 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 1019 1020 try 1021 { 1022 if (reserved3.value_or(0)) 1023 { 1024 return ipmi::responseInvalidFieldRequest(); 1025 } 1026 auto service = ipmi::getService(*busp, processorErrConfigIntf, 1027 processorErrConfigObjPath); 1028 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath, 1029 processorErrConfigIntf, "ResetOnCATERR", 1030 resetOnCATERR); 1031 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath, 1032 processorErrConfigIntf, "ResetOnERR2", 1033 resetOnERR2); 1034 if (clearCPUErrorCount.value_or(false)) 1035 { 1036 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath, 1037 processorErrConfigIntf, "ErrorCountCPU1", 1038 static_cast<uint8_t>(0)); 1039 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath, 1040 processorErrConfigIntf, "ErrorCountCPU2", 1041 static_cast<uint8_t>(0)); 1042 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath, 1043 processorErrConfigIntf, "ErrorCountCPU3", 1044 static_cast<uint8_t>(0)); 1045 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath, 1046 processorErrConfigIntf, "ErrorCountCPU4", 1047 static_cast<uint8_t>(0)); 1048 } 1049 if (clearCrashdumpCount.value_or(false)) 1050 { 1051 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath, 1052 processorErrConfigIntf, "CrashdumpCount", 1053 static_cast<uint8_t>(0)); 1054 } 1055 } 1056 catch (const std::exception& e) 1057 { 1058 phosphor::logging::log<phosphor::logging::level::ERR>( 1059 "Failed to set processor error config", 1060 phosphor::logging::entry("EXCEPTION=%s", e.what())); 1061 return ipmi::responseUnspecifiedError(); 1062 } 1063 1064 return ipmi::responseSuccess(); 1065 } 1066 1067 ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1068 ipmi_request_t request, 1069 ipmi_response_t response, 1070 ipmi_data_len_t dataLen, 1071 ipmi_context_t context) 1072 { 1073 GetOEMShutdownPolicyRes* resp = 1074 reinterpret_cast<GetOEMShutdownPolicyRes*>(response); 1075 1076 if (*dataLen != 0) 1077 { 1078 phosphor::logging::log<phosphor::logging::level::ERR>( 1079 "oem_get_shutdown_policy: invalid input len!"); 1080 *dataLen = 0; 1081 return IPMI_CC_REQ_DATA_LEN_INVALID; 1082 } 1083 1084 *dataLen = 0; 1085 1086 try 1087 { 1088 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1089 std::string service = getService(*dbus, oemShutdownPolicyIntf, 1090 oemShutdownPolicyObjPath); 1091 Value variant = getDbusProperty( 1092 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf, 1093 oemShutdownPolicyObjPathProp); 1094 1095 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy:: 1096 convertPolicyFromString(std::get<std::string>(variant)) == 1097 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy:: 1098 NoShutdownOnOCOT) 1099 { 1100 resp->policy = 0; 1101 } 1102 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy:: 1103 convertPolicyFromString(std::get<std::string>(variant)) == 1104 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy:: 1105 Policy::ShutdownOnOCOT) 1106 { 1107 resp->policy = 1; 1108 } 1109 else 1110 { 1111 phosphor::logging::log<phosphor::logging::level::ERR>( 1112 "oem_set_shutdown_policy: invalid property!", 1113 phosphor::logging::entry( 1114 "PROP=%s", std::get<std::string>(variant).c_str())); 1115 return IPMI_CC_UNSPECIFIED_ERROR; 1116 } 1117 // TODO needs to check if it is multi-node products, 1118 // policy is only supported on node 3/4 1119 resp->policySupport = shutdownPolicySupported; 1120 } 1121 catch (const sdbusplus::exception_t& e) 1122 { 1123 phosphor::logging::log<phosphor::logging::level::ERR>(e.description()); 1124 return IPMI_CC_UNSPECIFIED_ERROR; 1125 } 1126 1127 *dataLen = sizeof(GetOEMShutdownPolicyRes); 1128 return IPMI_CC_OK; 1129 } 1130 1131 ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1132 ipmi_request_t request, 1133 ipmi_response_t response, 1134 ipmi_data_len_t dataLen, 1135 ipmi_context_t context) 1136 { 1137 uint8_t* req = reinterpret_cast<uint8_t*>(request); 1138 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy = 1139 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy:: 1140 NoShutdownOnOCOT; 1141 1142 // TODO needs to check if it is multi-node products, 1143 // policy is only supported on node 3/4 1144 if (*dataLen != 1) 1145 { 1146 phosphor::logging::log<phosphor::logging::level::ERR>( 1147 "oem_set_shutdown_policy: invalid input len!"); 1148 *dataLen = 0; 1149 return IPMI_CC_REQ_DATA_LEN_INVALID; 1150 } 1151 1152 *dataLen = 0; 1153 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT)) 1154 { 1155 phosphor::logging::log<phosphor::logging::level::ERR>( 1156 "oem_set_shutdown_policy: invalid input!"); 1157 return IPMI_CC_INVALID_FIELD_REQUEST; 1158 } 1159 1160 if (*req == noShutdownOnOCOT) 1161 { 1162 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy:: 1163 Policy::NoShutdownOnOCOT; 1164 } 1165 else 1166 { 1167 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy:: 1168 Policy::ShutdownOnOCOT; 1169 } 1170 1171 try 1172 { 1173 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1174 std::string service = getService(*dbus, oemShutdownPolicyIntf, 1175 oemShutdownPolicyObjPath); 1176 setDbusProperty( 1177 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf, 1178 oemShutdownPolicyObjPathProp, 1179 sdbusplus::com::intel::Control::server::convertForMessage(policy)); 1180 } 1181 catch (const sdbusplus::exception_t& e) 1182 { 1183 phosphor::logging::log<phosphor::logging::level::ERR>(e.description()); 1184 return IPMI_CC_UNSPECIFIED_ERROR; 1185 } 1186 1187 return IPMI_CC_OK; 1188 } 1189 1190 /** @brief implementation for check the DHCP or not in IPv4 1191 * @param[in] Channel - Channel number 1192 * @returns true or false. 1193 */ 1194 static bool isDHCPEnabled(uint8_t Channel) 1195 { 1196 try 1197 { 1198 auto ethdevice = getChannelName(Channel); 1199 if (ethdevice.empty()) 1200 { 1201 return false; 1202 } 1203 auto ethIP = ethdevice + "/ipv4"; 1204 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1205 auto ethernetObj = getDbusObject(*dbus, networkIPIntf, networkRoot, 1206 ethIP); 1207 auto value = getDbusProperty(*dbus, networkService, ethernetObj.first, 1208 networkIPIntf, "Origin"); 1209 if (std::get<std::string>(value) == 1210 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP") 1211 { 1212 return true; 1213 } 1214 else 1215 { 1216 return false; 1217 } 1218 } 1219 catch (const sdbusplus::exception_t& e) 1220 { 1221 phosphor::logging::log<phosphor::logging::level::ERR>(e.description()); 1222 return true; 1223 } 1224 } 1225 1226 /** @brief implementes for check the DHCP or not in IPv6 1227 * @param[in] Channel - Channel number 1228 * @returns true or false. 1229 */ 1230 static bool isDHCPIPv6Enabled(uint8_t Channel) 1231 { 1232 try 1233 { 1234 auto ethdevice = getChannelName(Channel); 1235 if (ethdevice.empty()) 1236 { 1237 return false; 1238 } 1239 auto ethIP = ethdevice + "/ipv6"; 1240 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1241 auto objectInfo = getDbusObject(*dbus, networkIPIntf, networkRoot, 1242 ethIP); 1243 auto properties = getAllDbusProperties(*dbus, objectInfo.second, 1244 objectInfo.first, networkIPIntf); 1245 if (std::get<std::string>(properties["Origin"]) == 1246 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP") 1247 { 1248 return true; 1249 } 1250 else 1251 { 1252 return false; 1253 } 1254 } 1255 catch (const sdbusplus::exception_t& e) 1256 { 1257 phosphor::logging::log<phosphor::logging::level::ERR>(e.description()); 1258 return true; 1259 } 1260 } 1261 1262 /** @brief implementes the creating of default new user 1263 * @param[in] userName - new username in 16 bytes. 1264 * @param[in] userPassword - new password in 20 bytes 1265 * @returns ipmi completion code. 1266 */ 1267 ipmi::RspType<> ipmiOEMSetUser2Activation( 1268 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName, 1269 const SecureBuffer& userPassword) 1270 { 1271 if (userPassword.size() != ipmi::maxIpmi20PasswordSize) 1272 { 1273 return ipmi::responseReqDataLenInvalid(); 1274 } 1275 bool userState = false; 1276 // Check for System Interface not exist and LAN should be static 1277 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++) 1278 { 1279 ChannelInfo chInfo{}; 1280 try 1281 { 1282 getChannelInfo(channel, chInfo); 1283 } 1284 catch (const sdbusplus::exception_t& e) 1285 { 1286 phosphor::logging::log<phosphor::logging::level::ERR>( 1287 "ipmiOEMSetUser2Activation: Failed to get Channel Info", 1288 phosphor::logging::entry("MSG: %s", e.description())); 1289 return ipmi::response(ipmi::ccUnspecifiedError); 1290 } 1291 if (chInfo.mediumType == 1292 static_cast<uint8_t>(EChannelMediumType::systemInterface)) 1293 { 1294 phosphor::logging::log<phosphor::logging::level::ERR>( 1295 "ipmiOEMSetUser2Activation: system interface exist ."); 1296 return ipmi::response(ipmi::ccCommandNotAvailable); 1297 } 1298 else 1299 { 1300 if (chInfo.mediumType == 1301 static_cast<uint8_t>(EChannelMediumType::lan8032)) 1302 { 1303 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel)) 1304 { 1305 phosphor::logging::log<phosphor::logging::level::ERR>( 1306 "ipmiOEMSetUser2Activation: DHCP enabled ."); 1307 return ipmi::response(ipmi::ccCommandNotAvailable); 1308 } 1309 } 1310 } 1311 } 1312 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0; 1313 if (ipmi::ccSuccess == 1314 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers)) 1315 { 1316 if (enabledUsers > 1) 1317 { 1318 phosphor::logging::log<phosphor::logging::level::ERR>( 1319 "ipmiOEMSetUser2Activation: more than one user is enabled."); 1320 return ipmi::response(ipmi::ccCommandNotAvailable); 1321 } 1322 // Check the user 2 is enabled or not 1323 ipmiUserCheckEnabled(ipmiDefaultUserId, userState); 1324 if (userState == true) 1325 { 1326 phosphor::logging::log<phosphor::logging::level::ERR>( 1327 "ipmiOEMSetUser2Activation: user 2 already enabled ."); 1328 return ipmi::response(ipmi::ccCommandNotAvailable); 1329 } 1330 } 1331 else 1332 { 1333 return ipmi::response(ipmi::ccUnspecifiedError); 1334 } 1335 1336 #if BYTE_ORDER == LITTLE_ENDIAN 1337 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0}; 1338 #endif 1339 #if BYTE_ORDER == BIG_ENDIAN 1340 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN}; 1341 #endif 1342 1343 // ipmiUserSetUserName correctly handles char*, possibly non-null 1344 // terminated strings using ipmiMaxUserName size 1345 size_t nameLen = strnlen(reinterpret_cast<const char*>(userName.data()), 1346 sizeof(userName)); 1347 const std::string userNameRaw( 1348 reinterpret_cast<const char*>(userName.data()), nameLen); 1349 1350 if (ipmi::ccSuccess == ipmiUserSetUserName(ipmiDefaultUserId, userNameRaw)) 1351 { 1352 if (ipmi::ccSuccess == 1353 ipmiUserSetUserPassword( 1354 ipmiDefaultUserId, 1355 reinterpret_cast<const char*>(userPassword.data()))) 1356 { 1357 if (ipmi::ccSuccess == 1358 ipmiUserSetPrivilegeAccess( 1359 ipmiDefaultUserId, 1360 static_cast<uint8_t>(ipmi::EChannelID::chanLan1), 1361 privAccess, true)) 1362 { 1363 phosphor::logging::log<phosphor::logging::level::INFO>( 1364 "ipmiOEMSetUser2Activation: user created successfully "); 1365 1366 return ipmi::responseSuccess(); 1367 } 1368 } 1369 // we need to delete the default user id which added in this command as 1370 // password / priv setting is failed. 1371 ipmiUserSetUserName(ipmiDefaultUserId, static_cast<std::string>("")); 1372 phosphor::logging::log<phosphor::logging::level::ERR>( 1373 "ipmiOEMSetUser2Activation: password / priv setting is failed."); 1374 } 1375 else 1376 { 1377 phosphor::logging::log<phosphor::logging::level::ERR>( 1378 "ipmiOEMSetUser2Activation: Setting username failed."); 1379 } 1380 1381 return ipmi::response(ipmi::ccCommandNotAvailable); 1382 } 1383 1384 /** @brief implementes executing the linux command 1385 * @param[in] linux command 1386 * @returns status 1387 */ 1388 1389 static uint8_t executeCmd(const char* path) 1390 { 1391 boost::process::child execProg(path); 1392 execProg.wait(); 1393 1394 int retCode = execProg.exit_code(); 1395 if (retCode) 1396 { 1397 return ipmi::ccUnspecifiedError; 1398 } 1399 return ipmi::ccSuccess; 1400 } 1401 1402 /** @brief implementes ASD Security event logging 1403 * @param[in] Event message string 1404 * @param[in] Event Severity 1405 * @returns status 1406 */ 1407 1408 static void atScaleDebugEventlog(std::string msg, int severity) 1409 { 1410 std::string eventStr = "OpenBMC.0.1." + msg; 1411 sd_journal_send("MESSAGE=Security Event: %s", eventStr.c_str(), 1412 "PRIORITY=%i", severity, "REDFISH_MESSAGE_ID=%s", 1413 eventStr.c_str(), NULL); 1414 } 1415 1416 /** @brief implementes setting password for special user 1417 * @param[in] specialUserIndex 1418 * @param[in] userPassword - new password in 20 bytes 1419 * @returns ipmi completion code. 1420 */ 1421 ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx, 1422 uint8_t specialUserIndex, 1423 std::vector<uint8_t> userPassword) 1424 { 1425 ChannelInfo chInfo; 1426 ipmi_ret_t status = ipmi::ccSuccess; 1427 1428 try 1429 { 1430 getChannelInfo(ctx->channel, chInfo); 1431 } 1432 catch (const sdbusplus::exception_t& e) 1433 { 1434 phosphor::logging::log<phosphor::logging::level::ERR>( 1435 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info", 1436 phosphor::logging::entry("MSG: %s", e.description())); 1437 return ipmi::responseUnspecifiedError(); 1438 } 1439 if (chInfo.mediumType != 1440 static_cast<uint8_t>(EChannelMediumType::systemInterface)) 1441 { 1442 phosphor::logging::log<phosphor::logging::level::ERR>( 1443 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS " 1444 "interface"); 1445 return ipmi::responseCommandNotAvailable(); 1446 } 1447 1448 // 0 for root user and 1 for AtScaleDebug is allowed 1449 if (specialUserIndex > 1450 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser)) 1451 { 1452 phosphor::logging::log<phosphor::logging::level::ERR>( 1453 "ipmiOEMSetSpecialUserPassword: Invalid user account"); 1454 return ipmi::responseParmOutOfRange(); 1455 } 1456 if (userPassword.size() != 0) 1457 { 1458 constexpr uint8_t minPasswordSizeRequired = 6; 1459 SecureString passwd; 1460 if (userPassword.size() < minPasswordSizeRequired || 1461 userPassword.size() > ipmi::maxIpmi20PasswordSize) 1462 { 1463 OPENSSL_cleanse(userPassword.data(), userPassword.size()); 1464 return ipmi::responseReqDataLenInvalid(); 1465 } 1466 passwd.assign(reinterpret_cast<const char*>(userPassword.data()), 1467 userPassword.size()); 1468 // Clear sensitive data 1469 OPENSSL_cleanse(userPassword.data(), userPassword.size()); 1470 if (specialUserIndex == 1471 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser)) 1472 { 1473 status = ipmiSetSpecialUserPassword("asdbg", passwd); 1474 1475 atScaleDebugEventlog("AtScaleDebugSpecialUserEnabled", LOG_CRIT); 1476 } 1477 else 1478 { 1479 status = ipmiSetSpecialUserPassword("root", passwd); 1480 } 1481 return ipmi::response(status); 1482 } 1483 else 1484 { 1485 if (specialUserIndex == 1486 static_cast<uint8_t>(SpecialUserIndex::rootUser)) 1487 { 1488 status = executeCmd("passwd -d root"); 1489 } 1490 else 1491 { 1492 status = executeCmd("passwd -d asdbg"); 1493 1494 if (status == 0) 1495 { 1496 atScaleDebugEventlog("AtScaleDebugSpecialUserDisabled", 1497 LOG_INFO); 1498 } 1499 } 1500 return ipmi::response(status); 1501 } 1502 } 1503 1504 namespace ledAction 1505 { 1506 using namespace sdbusplus::xyz::openbmc_project::Led::server; 1507 std::map<Physical::Action, uint8_t> actionDbusToIpmi = { 1508 {Physical::Action::Off, 0}, 1509 {Physical::Action::On, 2}, 1510 {Physical::Action::Blink, 1}}; 1511 1512 std::map<uint8_t, std::string> offsetObjPath = { 1513 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}}; 1514 1515 } // namespace ledAction 1516 1517 int8_t getLEDState(sdbusplus::bus_t& bus, const std::string& intf, 1518 const std::string& objPath, uint8_t& state) 1519 { 1520 try 1521 { 1522 std::string service = getService(bus, intf, objPath); 1523 Value stateValue = getDbusProperty(bus, service, objPath, intf, 1524 "State"); 1525 std::string strState = std::get<std::string>(stateValue); 1526 state = ledAction::actionDbusToIpmi.at( 1527 sdbusplus::xyz::openbmc_project::Led::server::Physical:: 1528 convertActionFromString(strState)); 1529 } 1530 catch (const sdbusplus::exception_t& e) 1531 { 1532 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 1533 return -1; 1534 } 1535 return 0; 1536 } 1537 1538 ipmi::RspType<uint8_t> ipmiOEMGetLEDStatus() 1539 { 1540 uint8_t ledstate = 0; 1541 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status"); 1542 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1543 for (auto it = ledAction::offsetObjPath.begin(); 1544 it != ledAction::offsetObjPath.end(); ++it) 1545 { 1546 uint8_t state = 0; 1547 if (getLEDState(*dbus, ledIntf, it->second, state) == -1) 1548 { 1549 phosphor::logging::log<phosphor::logging::level::ERR>( 1550 "oem_get_led_status: fail to get ID LED status!"); 1551 return ipmi::responseUnspecifiedError(); 1552 } 1553 ledstate |= state << it->first; 1554 } 1555 return ipmi::responseSuccess(ledstate); 1556 } 1557 1558 ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1559 ipmi_request_t request, 1560 ipmi_response_t response, 1561 ipmi_data_len_t dataLen, 1562 ipmi_context_t context) 1563 { 1564 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request); 1565 uint8_t* resp = reinterpret_cast<uint8_t*>(response); 1566 1567 if (*dataLen == 0) 1568 { 1569 phosphor::logging::log<phosphor::logging::level::ERR>( 1570 "CfgHostSerial: invalid input len!", 1571 phosphor::logging::entry("LEN=%d", *dataLen)); 1572 return IPMI_CC_REQ_DATA_LEN_INVALID; 1573 } 1574 1575 switch (req->command) 1576 { 1577 case getHostSerialCfgCmd: 1578 { 1579 if (*dataLen != 1) 1580 { 1581 phosphor::logging::log<phosphor::logging::level::ERR>( 1582 "CfgHostSerial: invalid input len!"); 1583 *dataLen = 0; 1584 return IPMI_CC_REQ_DATA_LEN_INVALID; 1585 } 1586 1587 *dataLen = 0; 1588 1589 boost::process::ipstream is; 1590 std::vector<std::string> data; 1591 std::string line; 1592 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName, 1593 boost::process::std_out > is); 1594 1595 while (c1.running() && std::getline(is, line) && !line.empty()) 1596 { 1597 data.push_back(line); 1598 } 1599 1600 c1.wait(); 1601 if (c1.exit_code()) 1602 { 1603 phosphor::logging::log<phosphor::logging::level::ERR>( 1604 "CfgHostSerial:: error on execute", 1605 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd)); 1606 // Using the default value 1607 *resp = 0; 1608 } 1609 else 1610 { 1611 if (data.size() != 1) 1612 { 1613 phosphor::logging::log<phosphor::logging::level::ERR>( 1614 "CfgHostSerial:: error on read env"); 1615 return IPMI_CC_UNSPECIFIED_ERROR; 1616 } 1617 try 1618 { 1619 unsigned long tmp = std::stoul(data[0]); 1620 if (tmp > std::numeric_limits<uint8_t>::max()) 1621 { 1622 throw std::out_of_range("Out of range"); 1623 } 1624 *resp = static_cast<uint8_t>(tmp); 1625 } 1626 catch (const std::invalid_argument& e) 1627 { 1628 phosphor::logging::log<phosphor::logging::level::ERR>( 1629 "invalid config ", 1630 phosphor::logging::entry("ERR=%s", e.what())); 1631 return IPMI_CC_UNSPECIFIED_ERROR; 1632 } 1633 catch (const std::out_of_range& e) 1634 { 1635 phosphor::logging::log<phosphor::logging::level::ERR>( 1636 "out_of_range config ", 1637 phosphor::logging::entry("ERR=%s", e.what())); 1638 return IPMI_CC_UNSPECIFIED_ERROR; 1639 } 1640 } 1641 1642 *dataLen = 1; 1643 break; 1644 } 1645 case setHostSerialCfgCmd: 1646 { 1647 if (*dataLen != sizeof(CfgHostSerialReq)) 1648 { 1649 phosphor::logging::log<phosphor::logging::level::ERR>( 1650 "CfgHostSerial: invalid input len!"); 1651 *dataLen = 0; 1652 return IPMI_CC_REQ_DATA_LEN_INVALID; 1653 } 1654 1655 *dataLen = 0; 1656 1657 if (req->parameter > HostSerialCfgParamMax) 1658 { 1659 phosphor::logging::log<phosphor::logging::level::ERR>( 1660 "CfgHostSerial: invalid input!"); 1661 return IPMI_CC_INVALID_FIELD_REQUEST; 1662 } 1663 1664 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName, 1665 std::to_string(req->parameter)); 1666 1667 c1.wait(); 1668 if (c1.exit_code()) 1669 { 1670 phosphor::logging::log<phosphor::logging::level::ERR>( 1671 "CfgHostSerial:: error on execute", 1672 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd)); 1673 return IPMI_CC_UNSPECIFIED_ERROR; 1674 } 1675 break; 1676 } 1677 default: 1678 phosphor::logging::log<phosphor::logging::level::ERR>( 1679 "CfgHostSerial: invalid input!"); 1680 *dataLen = 0; 1681 return IPMI_CC_INVALID_FIELD_REQUEST; 1682 } 1683 1684 return IPMI_CC_OK; 1685 } 1686 1687 constexpr const char* thermalModeInterface = 1688 "xyz.openbmc_project.Control.ThermalMode"; 1689 constexpr const char* thermalModePath = 1690 "/xyz/openbmc_project/control/thermal_mode"; 1691 1692 bool getFanProfileInterface( 1693 sdbusplus::bus_t& bus, 1694 boost::container::flat_map<std::string, ipmi::DbusVariant>& resp) 1695 { 1696 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF, 1697 "GetAll"); 1698 call.append(thermalModeInterface); 1699 try 1700 { 1701 auto data = bus.call(call); 1702 data.read(resp); 1703 } 1704 catch (const sdbusplus::exception_t& e) 1705 { 1706 phosphor::logging::log<phosphor::logging::level::ERR>( 1707 "getFanProfileInterface: can't get thermal mode!", 1708 phosphor::logging::entry("ERR=%s", e.what())); 1709 return false; 1710 } 1711 return true; 1712 } 1713 1714 /**@brief implements the OEM set fan config. 1715 * @param selectedFanProfile - fan profile to enable 1716 * @param reserved1 1717 * @param performanceMode - Performance/Acoustic mode 1718 * @param reserved2 1719 * @param setPerformanceMode - set Performance/Acoustic mode 1720 * @param setFanProfile - set fan profile 1721 * 1722 * @return IPMI completion code. 1723 **/ 1724 ipmi::RspType<> ipmiOEMSetFanConfig(uint8_t selectedFanProfile, 1725 1726 uint2_t reserved1, bool performanceMode, 1727 uint3_t reserved2, bool setPerformanceMode, 1728 bool setFanProfile, 1729 std::optional<uint8_t> dimmGroupId, 1730 std::optional<uint32_t> dimmPresenceBitmap) 1731 { 1732 if (reserved1 || reserved2) 1733 { 1734 return ipmi::responseInvalidFieldRequest(); 1735 } 1736 1737 if (dimmGroupId) 1738 { 1739 if (*dimmGroupId >= maxCPUNum) 1740 { 1741 return ipmi::responseInvalidFieldRequest(); 1742 } 1743 if (!cpuPresent("cpu" + std::to_string(*dimmGroupId))) 1744 { 1745 return ipmi::responseInvalidFieldRequest(); 1746 } 1747 } 1748 1749 // todo: tell bios to only send first 2 bytes 1750 boost::container::flat_map<std::string, ipmi::DbusVariant> profileData; 1751 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1752 if (!getFanProfileInterface(*dbus, profileData)) 1753 { 1754 return ipmi::responseUnspecifiedError(); 1755 } 1756 1757 std::vector<std::string>* supported = 1758 std::get_if<std::vector<std::string>>(&profileData["Supported"]); 1759 if (supported == nullptr) 1760 { 1761 return ipmi::responseInvalidFieldRequest(); 1762 } 1763 std::string mode; 1764 if (setPerformanceMode) 1765 { 1766 if (performanceMode) 1767 { 1768 if (std::find(supported->begin(), supported->end(), 1769 "Performance") != supported->end()) 1770 { 1771 mode = "Performance"; 1772 } 1773 } 1774 else 1775 { 1776 if (std::find(supported->begin(), supported->end(), "Acoustic") != 1777 supported->end()) 1778 { 1779 mode = "Acoustic"; 1780 } 1781 } 1782 if (mode.empty()) 1783 { 1784 return ipmi::responseInvalidFieldRequest(); 1785 } 1786 1787 try 1788 { 1789 setDbusProperty(*dbus, settingsBusName, thermalModePath, 1790 thermalModeInterface, "Current", mode); 1791 } 1792 catch (const sdbusplus::exception_t& e) 1793 { 1794 phosphor::logging::log<phosphor::logging::level::ERR>( 1795 "ipmiOEMSetFanConfig: can't set thermal mode!", 1796 phosphor::logging::entry("EXCEPTION=%s", e.what())); 1797 return ipmi::responseResponseError(); 1798 } 1799 } 1800 1801 return ipmi::responseSuccess(); 1802 } 1803 1804 ipmi::RspType<uint8_t, // profile support map 1805 uint8_t, // fan control profile enable 1806 uint8_t, // flags 1807 uint32_t // dimm presence bit map 1808 > 1809 ipmiOEMGetFanConfig(uint8_t dimmGroupId) 1810 { 1811 if (dimmGroupId >= maxCPUNum) 1812 { 1813 return ipmi::responseInvalidFieldRequest(); 1814 } 1815 1816 bool cpuStatus = cpuPresent("cpu" + std::to_string(dimmGroupId)); 1817 1818 if (!cpuStatus) 1819 { 1820 return ipmi::responseInvalidFieldRequest(); 1821 } 1822 1823 boost::container::flat_map<std::string, ipmi::DbusVariant> profileData; 1824 1825 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1826 if (!getFanProfileInterface(*dbus, profileData)) 1827 { 1828 return ipmi::responseResponseError(); 1829 } 1830 1831 std::string* current = std::get_if<std::string>(&profileData["Current"]); 1832 1833 if (current == nullptr) 1834 { 1835 phosphor::logging::log<phosphor::logging::level::ERR>( 1836 "ipmiOEMGetFanConfig: can't get current mode!"); 1837 return ipmi::responseResponseError(); 1838 } 1839 bool performance = (*current == "Performance"); 1840 1841 uint8_t flags = 0; 1842 if (performance) 1843 { 1844 flags |= 1 << 2; 1845 } 1846 1847 constexpr uint8_t fanControlDefaultProfile = 0x80; 1848 constexpr uint8_t fanControlProfileState = 0x00; 1849 constexpr uint32_t dimmPresenceBitmap = 0x00; 1850 1851 return ipmi::responseSuccess(fanControlDefaultProfile, 1852 fanControlProfileState, flags, 1853 dimmPresenceBitmap); 1854 } 1855 constexpr const char* cfmLimitSettingPath = 1856 "/xyz/openbmc_project/control/cfm_limit"; 1857 constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit"; 1858 constexpr const size_t legacyExitAirSensorNumber = 0x2e; 1859 constexpr const size_t legacyPCHSensorNumber = 0x22; 1860 constexpr const char* exitAirPathName = "Exit_Air"; 1861 constexpr const char* pchPathName = "SSB_Temp"; 1862 constexpr const char* pidConfigurationIface = 1863 "xyz.openbmc_project.Configuration.Pid"; 1864 1865 static std::string getConfigPath(const std::string& name) 1866 { 1867 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1868 auto method = dbus->new_method_call("xyz.openbmc_project.ObjectMapper", 1869 "/xyz/openbmc_project/object_mapper", 1870 "xyz.openbmc_project.ObjectMapper", 1871 "GetSubTree"); 1872 1873 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface}); 1874 std::string path; 1875 GetSubTreeType resp; 1876 try 1877 { 1878 auto reply = dbus->call(method); 1879 reply.read(resp); 1880 } 1881 catch (const sdbusplus::exception_t&) 1882 { 1883 phosphor::logging::log<phosphor::logging::level::ERR>( 1884 "ipmiOEMGetFscParameter: mapper error"); 1885 }; 1886 auto config = std::find_if(resp.begin(), resp.end(), 1887 [&name](const auto& pair) { 1888 return pair.first.find(name) != std::string::npos; 1889 }); 1890 if (config != resp.end()) 1891 { 1892 path = std::move(config->first); 1893 } 1894 return path; 1895 } 1896 1897 // flat map to make alphabetical 1898 static boost::container::flat_map<std::string, PropertyMap> getPidConfigs() 1899 { 1900 boost::container::flat_map<std::string, PropertyMap> ret; 1901 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 1902 auto method = dbus->new_method_call("xyz.openbmc_project.ObjectMapper", 1903 "/xyz/openbmc_project/object_mapper", 1904 "xyz.openbmc_project.ObjectMapper", 1905 "GetSubTree"); 1906 1907 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface}); 1908 GetSubTreeType resp; 1909 1910 try 1911 { 1912 auto reply = dbus->call(method); 1913 reply.read(resp); 1914 } 1915 catch (const sdbusplus::exception_t&) 1916 { 1917 phosphor::logging::log<phosphor::logging::level::ERR>( 1918 "getFanConfigPaths: mapper error"); 1919 }; 1920 for (const auto& [path, objects] : resp) 1921 { 1922 if (objects.empty()) 1923 { 1924 continue; // should be impossible 1925 } 1926 1927 try 1928 { 1929 ret.emplace(path, 1930 getAllDbusProperties(*dbus, objects[0].first, path, 1931 pidConfigurationIface)); 1932 } 1933 catch (const sdbusplus::exception_t& e) 1934 { 1935 phosphor::logging::log<phosphor::logging::level::ERR>( 1936 "getPidConfigs: can't get DbusProperties!", 1937 phosphor::logging::entry("ERR=%s", e.what())); 1938 } 1939 } 1940 return ret; 1941 } 1942 1943 ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void) 1944 { 1945 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs(); 1946 if (data.empty()) 1947 { 1948 return ipmi::responseResponseError(); 1949 } 1950 uint8_t minOffset = std::numeric_limits<uint8_t>::max(); 1951 for (const auto& [_, pid] : data) 1952 { 1953 auto findClass = pid.find("Class"); 1954 if (findClass == pid.end()) 1955 { 1956 phosphor::logging::log<phosphor::logging::level::ERR>( 1957 "ipmiOEMGetFscParameter: found illegal pid " 1958 "configurations"); 1959 return ipmi::responseResponseError(); 1960 } 1961 std::string type = std::get<std::string>(findClass->second); 1962 if (type == "fan") 1963 { 1964 auto findOutLimit = pid.find("OutLimitMin"); 1965 if (findOutLimit == pid.end()) 1966 { 1967 phosphor::logging::log<phosphor::logging::level::ERR>( 1968 "ipmiOEMGetFscParameter: found illegal pid " 1969 "configurations"); 1970 return ipmi::responseResponseError(); 1971 } 1972 // get the min out of all the offsets 1973 minOffset = std::min( 1974 minOffset, 1975 static_cast<uint8_t>(std::get<double>(findOutLimit->second))); 1976 } 1977 } 1978 if (minOffset == std::numeric_limits<uint8_t>::max()) 1979 { 1980 phosphor::logging::log<phosphor::logging::level::ERR>( 1981 "ipmiOEMGetFscParameter: found no fan configurations!"); 1982 return ipmi::responseResponseError(); 1983 } 1984 1985 return ipmi::responseSuccess(minOffset); 1986 } 1987 1988 ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset) 1989 { 1990 constexpr uint8_t maxFanSpeedOffset = 100; 1991 if (offset > maxFanSpeedOffset) 1992 { 1993 phosphor::logging::log<phosphor::logging::level::ERR>( 1994 "ipmiOEMSetFanSpeedOffset: fan offset greater than limit"); 1995 return ipmi::responseInvalidFieldRequest(); 1996 } 1997 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs(); 1998 if (data.empty()) 1999 { 2000 phosphor::logging::log<phosphor::logging::level::ERR>( 2001 "ipmiOEMSetFanSpeedOffset: found no pid configurations!"); 2002 return ipmi::responseResponseError(); 2003 } 2004 2005 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 2006 bool found = false; 2007 for (const auto& [path, pid] : data) 2008 { 2009 auto findClass = pid.find("Class"); 2010 if (findClass == pid.end()) 2011 { 2012 phosphor::logging::log<phosphor::logging::level::ERR>( 2013 "ipmiOEMSetFanSpeedOffset: found illegal pid " 2014 "configurations"); 2015 return ipmi::responseResponseError(); 2016 } 2017 std::string type = std::get<std::string>(findClass->second); 2018 if (type == "fan") 2019 { 2020 auto findOutLimit = pid.find("OutLimitMin"); 2021 if (findOutLimit == pid.end()) 2022 { 2023 phosphor::logging::log<phosphor::logging::level::ERR>( 2024 "ipmiOEMSetFanSpeedOffset: found illegal pid " 2025 "configurations"); 2026 return ipmi::responseResponseError(); 2027 } 2028 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", 2029 path, pidConfigurationIface, "OutLimitMin", 2030 static_cast<double>(offset)); 2031 found = true; 2032 } 2033 } 2034 if (!found) 2035 { 2036 phosphor::logging::log<phosphor::logging::level::ERR>( 2037 "ipmiOEMSetFanSpeedOffset: set no fan offsets"); 2038 return ipmi::responseResponseError(); 2039 } 2040 2041 return ipmi::responseSuccess(); 2042 } 2043 2044 ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1, 2045 uint8_t param2) 2046 { 2047 constexpr const size_t disableLimiting = 0x0; 2048 2049 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 2050 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol)) 2051 { 2052 std::string pathName; 2053 if (param1 == legacyExitAirSensorNumber) 2054 { 2055 pathName = exitAirPathName; 2056 } 2057 else if (param1 == legacyPCHSensorNumber) 2058 { 2059 pathName = pchPathName; 2060 } 2061 else 2062 { 2063 return ipmi::responseParmOutOfRange(); 2064 } 2065 std::string path = getConfigPath(pathName); 2066 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path, 2067 pidConfigurationIface, "SetPoint", 2068 static_cast<double>(param2)); 2069 return ipmi::responseSuccess(); 2070 } 2071 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm)) 2072 { 2073 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8); 2074 2075 // must be greater than 50 based on eps 2076 if (cfm < 50 && cfm != disableLimiting) 2077 { 2078 return ipmi::responseParmOutOfRange(); 2079 } 2080 2081 try 2082 { 2083 ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath, 2084 cfmLimitIface, "Limit", 2085 static_cast<double>(cfm)); 2086 } 2087 catch (const sdbusplus::exception_t& e) 2088 { 2089 phosphor::logging::log<phosphor::logging::level::ERR>( 2090 "ipmiOEMSetFscParameter: can't set cfm setting!", 2091 phosphor::logging::entry("ERR=%s", e.what())); 2092 return ipmi::responseResponseError(); 2093 } 2094 return ipmi::responseSuccess(); 2095 } 2096 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm)) 2097 { 2098 constexpr const size_t maxDomainCount = 8; 2099 uint8_t requestedDomainMask = param1; 2100 boost::container::flat_map data = getPidConfigs(); 2101 if (data.empty()) 2102 { 2103 phosphor::logging::log<phosphor::logging::level::ERR>( 2104 "ipmiOEMSetFscParameter: found no pid configurations!"); 2105 return ipmi::responseResponseError(); 2106 } 2107 size_t count = 0; 2108 for (const auto& [path, pid] : data) 2109 { 2110 auto findClass = pid.find("Class"); 2111 if (findClass == pid.end()) 2112 { 2113 phosphor::logging::log<phosphor::logging::level::ERR>( 2114 "ipmiOEMSetFscParameter: found illegal pid " 2115 "configurations"); 2116 return ipmi::responseResponseError(); 2117 } 2118 std::string type = std::get<std::string>(findClass->second); 2119 if (type == "fan") 2120 { 2121 if (requestedDomainMask & (1 << count)) 2122 { 2123 ipmi::setDbusProperty( 2124 *dbus, "xyz.openbmc_project.EntityManager", path, 2125 pidConfigurationIface, "OutLimitMax", 2126 static_cast<double>(param2)); 2127 } 2128 count++; 2129 } 2130 } 2131 return ipmi::responseSuccess(); 2132 } 2133 else 2134 { 2135 // todo other command parts possibly 2136 // tcontrol is handled in peci now 2137 // fan speed offset not implemented yet 2138 // domain pwm limit not implemented 2139 return ipmi::responseParmOutOfRange(); 2140 } 2141 } 2142 2143 ipmi::RspType< 2144 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>> 2145 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param) 2146 { 2147 constexpr uint8_t legacyDefaultSetpoint = -128; 2148 2149 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 2150 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol)) 2151 { 2152 if (!param) 2153 { 2154 return ipmi::responseReqDataLenInvalid(); 2155 } 2156 2157 std::string pathName; 2158 2159 if (*param == legacyExitAirSensorNumber) 2160 { 2161 pathName = exitAirPathName; 2162 } 2163 else if (*param == legacyPCHSensorNumber) 2164 { 2165 pathName = pchPathName; 2166 } 2167 else 2168 { 2169 return ipmi::responseParmOutOfRange(); 2170 } 2171 2172 uint8_t setpoint = legacyDefaultSetpoint; 2173 std::string path = getConfigPath(pathName); 2174 if (path.size()) 2175 { 2176 Value val = ipmi::getDbusProperty( 2177 *dbus, "xyz.openbmc_project.EntityManager", path, 2178 pidConfigurationIface, "SetPoint"); 2179 setpoint = std::floor(std::get<double>(val) + 0.5); 2180 } 2181 2182 // old implementation used to return the "default" and current, we 2183 // don't make the default readily available so just make both the 2184 // same 2185 2186 return ipmi::responseSuccess( 2187 std::array<uint8_t, 2>{setpoint, setpoint}); 2188 } 2189 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm)) 2190 { 2191 constexpr const size_t maxDomainCount = 8; 2192 2193 if (!param) 2194 { 2195 return ipmi::responseReqDataLenInvalid(); 2196 } 2197 uint8_t requestedDomain = *param; 2198 if (requestedDomain >= maxDomainCount) 2199 { 2200 return ipmi::responseInvalidFieldRequest(); 2201 } 2202 2203 boost::container::flat_map data = getPidConfigs(); 2204 if (data.empty()) 2205 { 2206 phosphor::logging::log<phosphor::logging::level::ERR>( 2207 "ipmiOEMGetFscParameter: found no pid configurations!"); 2208 return ipmi::responseResponseError(); 2209 } 2210 size_t count = 0; 2211 for (const auto& [_, pid] : data) 2212 { 2213 auto findClass = pid.find("Class"); 2214 if (findClass == pid.end()) 2215 { 2216 phosphor::logging::log<phosphor::logging::level::ERR>( 2217 "ipmiOEMGetFscParameter: found illegal pid " 2218 "configurations"); 2219 return ipmi::responseResponseError(); 2220 } 2221 std::string type = std::get<std::string>(findClass->second); 2222 if (type == "fan") 2223 { 2224 if (requestedDomain == count) 2225 { 2226 auto findOutLimit = pid.find("OutLimitMax"); 2227 if (findOutLimit == pid.end()) 2228 { 2229 phosphor::logging::log<phosphor::logging::level::ERR>( 2230 "ipmiOEMGetFscParameter: found illegal pid " 2231 "configurations"); 2232 return ipmi::responseResponseError(); 2233 } 2234 2235 return ipmi::responseSuccess( 2236 static_cast<uint8_t>(std::floor( 2237 std::get<double>(findOutLimit->second) + 0.5))); 2238 } 2239 else 2240 { 2241 count++; 2242 } 2243 } 2244 } 2245 2246 return ipmi::responseInvalidFieldRequest(); 2247 } 2248 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm)) 2249 { 2250 /* 2251 DataLen should be 1, but host is sending us an extra bit. As the 2252 previous behavior didn't seem to prevent this, ignore the check for 2253 now. 2254 2255 if (param) 2256 { 2257 phosphor::logging::log<phosphor::logging::level::ERR>( 2258 "ipmiOEMGetFscParameter: invalid input len!"); 2259 return IPMI_CC_REQ_DATA_LEN_INVALID; 2260 } 2261 */ 2262 Value cfmLimit; 2263 Value cfmMaximum; 2264 try 2265 { 2266 cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName, 2267 cfmLimitSettingPath, cfmLimitIface, 2268 "Limit"); 2269 cfmMaximum = ipmi::getDbusProperty( 2270 *dbus, "xyz.openbmc_project.ExitAirTempSensor", 2271 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit"); 2272 } 2273 catch (const sdbusplus::exception_t& e) 2274 { 2275 phosphor::logging::log<phosphor::logging::level::ERR>( 2276 "ipmiOEMGetFscParameter: can't get cfm setting!", 2277 phosphor::logging::entry("ERR=%s", e.what())); 2278 return ipmi::responseResponseError(); 2279 } 2280 2281 double cfmMax = std::get<double>(cfmMaximum); 2282 double cfmLim = std::get<double>(cfmLimit); 2283 2284 cfmLim = std::floor(cfmLim + 0.5); 2285 cfmMax = std::floor(cfmMax + 0.5); 2286 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim); 2287 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax); 2288 2289 return ipmi::responseSuccess( 2290 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp}); 2291 } 2292 2293 else 2294 { 2295 // todo other command parts possibly 2296 // domain pwm limit not implemented 2297 return ipmi::responseParmOutOfRange(); 2298 } 2299 } 2300 2301 using crConfigVariant = ipmi::DbusVariant; 2302 2303 int setCRConfig(ipmi::Context::ptr ctx, const std::string& property, 2304 const crConfigVariant& value, 2305 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT) 2306 { 2307 boost::system::error_code ec; 2308 ctx->bus->yield_method_call<void>( 2309 ctx->yield, ec, "xyz.openbmc_project.PSURedundancy", 2310 "/xyz/openbmc_project/control/power_supply_redundancy", 2311 "org.freedesktop.DBus.Properties", "Set", 2312 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property, value); 2313 if (ec) 2314 { 2315 phosphor::logging::log<phosphor::logging::level::ERR>( 2316 "Failed to set dbus property to cold redundancy"); 2317 return -1; 2318 } 2319 2320 return 0; 2321 } 2322 2323 int getCRConfig( 2324 ipmi::Context::ptr ctx, const std::string& property, crConfigVariant& value, 2325 const std::string& service = "xyz.openbmc_project.PSURedundancy", 2326 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT) 2327 { 2328 boost::system::error_code ec; 2329 value = ctx->bus->yield_method_call<crConfigVariant>( 2330 ctx->yield, ec, service, 2331 "/xyz/openbmc_project/control/power_supply_redundancy", 2332 "org.freedesktop.DBus.Properties", "Get", 2333 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property); 2334 if (ec) 2335 { 2336 phosphor::logging::log<phosphor::logging::level::ERR>( 2337 "Failed to get dbus property to cold redundancy"); 2338 return -1; 2339 } 2340 return 0; 2341 } 2342 2343 uint8_t getPSUCount(void) 2344 { 2345 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 2346 ipmi::Value num; 2347 try 2348 { 2349 num = ipmi::getDbusProperty( 2350 *dbus, "xyz.openbmc_project.PSURedundancy", 2351 "/xyz/openbmc_project/control/power_supply_redundancy", 2352 "xyz.openbmc_project.Control.PowerSupplyRedundancy", "PSUNumber"); 2353 } 2354 catch (const sdbusplus::exception_t& e) 2355 { 2356 phosphor::logging::log<phosphor::logging::level::ERR>( 2357 "Failed to get PSUNumber property from dbus interface"); 2358 return 0; 2359 } 2360 uint8_t* pNum = std::get_if<uint8_t>(&num); 2361 if (!pNum) 2362 { 2363 phosphor::logging::log<phosphor::logging::level::ERR>( 2364 "Error to get PSU Number"); 2365 return 0; 2366 } 2367 return *pNum; 2368 } 2369 2370 bool validateCRAlgo(std::vector<uint8_t>& conf, uint8_t num) 2371 { 2372 if (conf.size() < num) 2373 { 2374 phosphor::logging::log<phosphor::logging::level::ERR>( 2375 "Invalid PSU Ranking"); 2376 return false; 2377 } 2378 std::set<uint8_t> confSet; 2379 for (uint8_t i = 0; i < num; i++) 2380 { 2381 if (conf[i] > num) 2382 { 2383 phosphor::logging::log<phosphor::logging::level::ERR>( 2384 "PSU Ranking is larger than current PSU number"); 2385 return false; 2386 } 2387 confSet.emplace(conf[i]); 2388 } 2389 2390 if (confSet.size() != num) 2391 { 2392 phosphor::logging::log<phosphor::logging::level::ERR>( 2393 "duplicate PSU Ranking"); 2394 return false; 2395 } 2396 return true; 2397 } 2398 2399 enum class crParameter 2400 { 2401 crStatus = 0, 2402 crFeature = 1, 2403 rotationFeature = 2, 2404 rotationAlgo = 3, 2405 rotationPeriod = 4, 2406 numOfPSU = 5, 2407 rotationRankOrderEffective = 6 2408 }; 2409 2410 constexpr ipmi::Cc ccParameterNotSupported = 0x80; 2411 static const constexpr uint32_t oneDay = 0x15180; 2412 static const constexpr uint32_t oneMonth = 0xf53700; 2413 static const constexpr uint8_t userSpecific = 0x01; 2414 static const constexpr uint8_t crSetCompleted = 0; 2415 ipmi::RspType<uint8_t> ipmiOEMSetCRConfig(ipmi::Context::ptr ctx, 2416 uint8_t parameter, 2417 ipmi::message::Payload& payload) 2418 { 2419 switch (static_cast<crParameter>(parameter)) 2420 { 2421 case crParameter::rotationFeature: 2422 { 2423 uint8_t param1; 2424 if (payload.unpack(param1) || !payload.fullyUnpacked()) 2425 { 2426 return ipmi::responseReqDataLenInvalid(); 2427 } 2428 // Rotation Enable can only be true or false 2429 if (param1 > 1) 2430 { 2431 return ipmi::responseInvalidFieldRequest(); 2432 } 2433 if (setCRConfig(ctx, "RotationEnabled", static_cast<bool>(param1))) 2434 { 2435 return ipmi::responseResponseError(); 2436 } 2437 break; 2438 } 2439 case crParameter::rotationAlgo: 2440 { 2441 // Rotation Algorithm can only be 0-BMC Specific or 1-User Specific 2442 std::string algoName; 2443 uint8_t param1; 2444 if (payload.unpack(param1)) 2445 { 2446 return ipmi::responseReqDataLenInvalid(); 2447 } 2448 switch (param1) 2449 { 2450 case 0: 2451 algoName = "xyz.openbmc_project.Control." 2452 "PowerSupplyRedundancy.Algo.bmcSpecific"; 2453 break; 2454 case 1: 2455 algoName = "xyz.openbmc_project.Control." 2456 "PowerSupplyRedundancy.Algo.userSpecific"; 2457 break; 2458 default: 2459 return ipmi::responseInvalidFieldRequest(); 2460 } 2461 if (setCRConfig(ctx, "RotationAlgorithm", algoName)) 2462 { 2463 return ipmi::responseResponseError(); 2464 } 2465 2466 uint8_t numberOfPSU = getPSUCount(); 2467 if (!numberOfPSU) 2468 { 2469 return ipmi::responseResponseError(); 2470 } 2471 std::vector<uint8_t> rankOrder; 2472 2473 if (param1 == userSpecific) 2474 { 2475 if (payload.unpack(rankOrder) || !payload.fullyUnpacked()) 2476 { 2477 ipmi::responseReqDataLenInvalid(); 2478 } 2479 if (rankOrder.size() != numberOfPSU) 2480 { 2481 return ipmi::responseReqDataLenInvalid(); 2482 } 2483 2484 if (!validateCRAlgo(rankOrder, numberOfPSU)) 2485 { 2486 return ipmi::responseInvalidFieldRequest(); 2487 } 2488 } 2489 else 2490 { 2491 if (rankOrder.size() > 0) 2492 { 2493 return ipmi::responseReqDataLenInvalid(); 2494 } 2495 for (uint8_t i = 1; i <= numberOfPSU; i++) 2496 { 2497 rankOrder.emplace_back(i); 2498 } 2499 } 2500 if (setCRConfig(ctx, "RotationRankOrder", rankOrder)) 2501 { 2502 return ipmi::responseResponseError(); 2503 } 2504 break; 2505 } 2506 case crParameter::rotationPeriod: 2507 { 2508 // Minimum Rotation period is One day (86400 seconds) and Max 2509 // Rotation Period is 6 month (0xf53700 seconds) 2510 uint32_t period; 2511 if (payload.unpack(period) || !payload.fullyUnpacked()) 2512 { 2513 return ipmi::responseReqDataLenInvalid(); 2514 } 2515 if ((period < oneDay) || (period > oneMonth)) 2516 { 2517 return ipmi::responseInvalidFieldRequest(); 2518 } 2519 if (setCRConfig(ctx, "PeriodOfRotation", period)) 2520 { 2521 return ipmi::responseResponseError(); 2522 } 2523 break; 2524 } 2525 default: 2526 { 2527 return ipmi::response(ccParameterNotSupported); 2528 } 2529 } 2530 2531 return ipmi::responseSuccess(crSetCompleted); 2532 } 2533 2534 ipmi::RspType<uint8_t, std::variant<uint8_t, uint32_t, std::vector<uint8_t>>> 2535 ipmiOEMGetCRConfig(ipmi::Context::ptr ctx, uint8_t parameter) 2536 { 2537 crConfigVariant value; 2538 switch (static_cast<crParameter>(parameter)) 2539 { 2540 case crParameter::crStatus: 2541 { 2542 if (getCRConfig(ctx, "ColdRedundancyStatus", value)) 2543 { 2544 return ipmi::responseResponseError(); 2545 } 2546 std::string* pStatus = std::get_if<std::string>(&value); 2547 if (!pStatus) 2548 { 2549 phosphor::logging::log<phosphor::logging::level::ERR>( 2550 "Error to get ColdRedundancyStatus property"); 2551 return ipmi::responseResponseError(); 2552 } 2553 namespace server = sdbusplus::xyz::openbmc_project::Control::server; 2554 auto status = 2555 server::PowerSupplyRedundancy::convertStatusFromString( 2556 *pStatus); 2557 switch (status) 2558 { 2559 case server::PowerSupplyRedundancy::Status::inProgress: 2560 return ipmi::responseSuccess(parameter, 2561 static_cast<uint8_t>(1)); 2562 2563 case server::PowerSupplyRedundancy::Status::completed: 2564 return ipmi::responseSuccess(parameter, 2565 static_cast<uint8_t>(0)); 2566 default: 2567 phosphor::logging::log<phosphor::logging::level::ERR>( 2568 "Error to get valid status"); 2569 return ipmi::responseResponseError(); 2570 } 2571 } 2572 case crParameter::crFeature: 2573 { 2574 if (getCRConfig(ctx, "PowerSupplyRedundancyEnabled", value)) 2575 { 2576 return ipmi::responseResponseError(); 2577 } 2578 bool* pResponse = std::get_if<bool>(&value); 2579 if (!pResponse) 2580 { 2581 phosphor::logging::log<phosphor::logging::level::ERR>( 2582 "Error to get PowerSupplyRedundancyEnabled property"); 2583 return ipmi::responseResponseError(); 2584 } 2585 2586 return ipmi::responseSuccess(parameter, 2587 static_cast<uint8_t>(*pResponse)); 2588 } 2589 case crParameter::rotationFeature: 2590 { 2591 if (getCRConfig(ctx, "RotationEnabled", value)) 2592 { 2593 return ipmi::responseResponseError(); 2594 } 2595 bool* pResponse = std::get_if<bool>(&value); 2596 if (!pResponse) 2597 { 2598 phosphor::logging::log<phosphor::logging::level::ERR>( 2599 "Error to get RotationEnabled property"); 2600 return ipmi::responseResponseError(); 2601 } 2602 return ipmi::responseSuccess(parameter, 2603 static_cast<uint8_t>(*pResponse)); 2604 } 2605 case crParameter::rotationAlgo: 2606 { 2607 if (getCRConfig(ctx, "RotationAlgorithm", value)) 2608 { 2609 return ipmi::responseResponseError(); 2610 } 2611 2612 std::string* pAlgo = std::get_if<std::string>(&value); 2613 if (!pAlgo) 2614 { 2615 phosphor::logging::log<phosphor::logging::level::ERR>( 2616 "Error to get RotationAlgorithm property"); 2617 return ipmi::responseResponseError(); 2618 } 2619 std::vector<uint8_t> response; 2620 namespace server = sdbusplus::xyz::openbmc_project::Control::server; 2621 auto algo = 2622 server::PowerSupplyRedundancy::convertAlgoFromString(*pAlgo); 2623 2624 switch (algo) 2625 { 2626 case server::PowerSupplyRedundancy::Algo::bmcSpecific: 2627 response.push_back(0); 2628 break; 2629 case server::PowerSupplyRedundancy::Algo::userSpecific: 2630 response.push_back(1); 2631 break; 2632 default: 2633 phosphor::logging::log<phosphor::logging::level::ERR>( 2634 "Error to get valid algo"); 2635 return ipmi::responseResponseError(); 2636 } 2637 2638 if (getCRConfig(ctx, "RotationRankOrder", value)) 2639 { 2640 return ipmi::responseResponseError(); 2641 } 2642 std::vector<uint8_t>* pResponse = 2643 std::get_if<std::vector<uint8_t>>(&value); 2644 if (!pResponse) 2645 { 2646 phosphor::logging::log<phosphor::logging::level::ERR>( 2647 "Error to get RotationRankOrder property"); 2648 return ipmi::responseResponseError(); 2649 } 2650 2651 std::copy(pResponse->begin(), pResponse->end(), 2652 std::back_inserter(response)); 2653 2654 return ipmi::responseSuccess(parameter, response); 2655 } 2656 case crParameter::rotationPeriod: 2657 { 2658 if (getCRConfig(ctx, "PeriodOfRotation", value)) 2659 { 2660 return ipmi::responseResponseError(); 2661 } 2662 uint32_t* pResponse = std::get_if<uint32_t>(&value); 2663 if (!pResponse) 2664 { 2665 phosphor::logging::log<phosphor::logging::level::ERR>( 2666 "Error to get RotationAlgorithm property"); 2667 return ipmi::responseResponseError(); 2668 } 2669 return ipmi::responseSuccess(parameter, *pResponse); 2670 } 2671 case crParameter::numOfPSU: 2672 { 2673 uint8_t numberOfPSU = getPSUCount(); 2674 if (!numberOfPSU) 2675 { 2676 return ipmi::responseResponseError(); 2677 } 2678 return ipmi::responseSuccess(parameter, numberOfPSU); 2679 } 2680 case crParameter::rotationRankOrderEffective: 2681 { 2682 if (getCRConfig(ctx, "RotationRankOrder", value, 2683 "xyz.openbmc_project.PSURedundancy")) 2684 { 2685 return ipmi::responseResponseError(); 2686 } 2687 std::vector<uint8_t>* pResponse = 2688 std::get_if<std::vector<uint8_t>>(&value); 2689 if (!pResponse) 2690 { 2691 phosphor::logging::log<phosphor::logging::level::ERR>( 2692 "Error to get effective RotationRankOrder property"); 2693 return ipmi::responseResponseError(); 2694 } 2695 return ipmi::responseSuccess(parameter, *pResponse); 2696 } 2697 default: 2698 { 2699 return ipmi::response(ccParameterNotSupported); 2700 } 2701 } 2702 } 2703 2704 ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType, 2705 uint8_t faultState, 2706 uint8_t faultGroup, 2707 std::array<uint8_t, 8>& ledStateData) 2708 { 2709 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max); 2710 static const std::array<std::string, maxFaultType> faultNames = { 2711 "faultFan", "faultTemp", "faultPower", 2712 "faultDriveSlot", "faultSoftware", "faultMemory"}; 2713 2714 constexpr uint8_t maxFaultSource = 0x4; 2715 constexpr uint8_t skipLEDs = 0xFF; 2716 constexpr uint8_t pinSize = 64; 2717 constexpr uint8_t groupSize = 16; 2718 constexpr uint8_t groupNum = 5; // 4 for fault memory, 1 for faultFan 2719 2720 // same pin names need to be defined in dts file 2721 static const std::array<std::array<std::string, groupSize>, groupNum> 2722 faultLedPinNames = {{ 2723 "LED_CPU1_CH1_DIMM1_FAULT", 2724 "LED_CPU1_CH1_DIMM2_FAULT", 2725 "LED_CPU1_CH2_DIMM1_FAULT", 2726 "LED_CPU1_CH2_DIMM2_FAULT", 2727 "LED_CPU1_CH3_DIMM1_FAULT", 2728 "LED_CPU1_CH3_DIMM2_FAULT", 2729 "LED_CPU1_CH4_DIMM1_FAULT", 2730 "LED_CPU1_CH4_DIMM2_FAULT", 2731 "LED_CPU1_CH5_DIMM1_FAULT", 2732 "LED_CPU1_CH5_DIMM2_FAULT", 2733 "LED_CPU1_CH6_DIMM1_FAULT", 2734 "LED_CPU1_CH6_DIMM2_FAULT", 2735 "", 2736 "", 2737 "", 2738 "", // end of group1 2739 "LED_CPU2_CH1_DIMM1_FAULT", 2740 "LED_CPU2_CH1_DIMM2_FAULT", 2741 "LED_CPU2_CH2_DIMM1_FAULT", 2742 "LED_CPU2_CH2_DIMM2_FAULT", 2743 "LED_CPU2_CH3_DIMM1_FAULT", 2744 "LED_CPU2_CH3_DIMM2_FAULT", 2745 "LED_CPU2_CH4_DIMM1_FAULT", 2746 "LED_CPU2_CH4_DIMM2_FAULT", 2747 "LED_CPU2_CH5_DIMM1_FAULT", 2748 "LED_CPU2_CH5_DIMM2_FAULT", 2749 "LED_CPU2_CH6_DIMM1_FAULT", 2750 "LED_CPU2_CH6_DIMM2_FAULT", 2751 "", 2752 "", 2753 "", 2754 "", // endof group2 2755 "LED_CPU3_CH1_DIMM1_FAULT", 2756 "LED_CPU3_CH1_DIMM2_FAULT", 2757 "LED_CPU3_CH2_DIMM1_FAULT", 2758 "LED_CPU3_CH2_DIMM2_FAULT", 2759 "LED_CPU3_CH3_DIMM1_FAULT", 2760 "LED_CPU3_CH3_DIMM2_FAULT", 2761 "LED_CPU3_CH4_DIMM1_FAULT", 2762 "LED_CPU3_CH4_DIMM2_FAULT", 2763 "LED_CPU3_CH5_DIMM1_FAULT", 2764 "LED_CPU3_CH5_DIMM2_FAULT", 2765 "LED_CPU3_CH6_DIMM1_FAULT", 2766 "LED_CPU3_CH6_DIMM2_FAULT", 2767 "", 2768 "", 2769 "", 2770 "", // end of group3 2771 "LED_CPU4_CH1_DIMM1_FAULT", 2772 "LED_CPU4_CH1_DIMM2_FAULT", 2773 "LED_CPU4_CH2_DIMM1_FAULT", 2774 "LED_CPU4_CH2_DIMM2_FAULT", 2775 "LED_CPU4_CH3_DIMM1_FAULT", 2776 "LED_CPU4_CH3_DIMM2_FAULT", 2777 "LED_CPU4_CH4_DIMM1_FAULT", 2778 "LED_CPU4_CH4_DIMM2_FAULT", 2779 "LED_CPU4_CH5_DIMM1_FAULT", 2780 "LED_CPU4_CH5_DIMM2_FAULT", 2781 "LED_CPU4_CH6_DIMM1_FAULT", 2782 "LED_CPU4_CH6_DIMM2_FAULT", 2783 "", 2784 "", 2785 "", 2786 "", // end of group4 2787 "LED_FAN1_FAULT", 2788 "LED_FAN2_FAULT", 2789 "LED_FAN3_FAULT", 2790 "LED_FAN4_FAULT", 2791 "LED_FAN5_FAULT", 2792 "LED_FAN6_FAULT", 2793 "LED_FAN7_FAULT", 2794 "LED_FAN8_FAULT", 2795 "", 2796 "", 2797 "", 2798 "", 2799 "", 2800 "", 2801 "", 2802 "" // end of group5 2803 }}; 2804 2805 // Validate the source, fault type -- 2806 // (Byte 1) sourceId: Unspecified, Hot-Swap Controller 0, Hot-Swap 2807 // Controller 1, BIOS (Byte 2) fault type: fan, temperature, power, 2808 // driveslot, software, memory (Byte 3) FaultState: OK, Degraded, 2809 // Non-Critical, Critical, Non-Recoverable, (Byte 4) is faultGroup, 2810 // definition differs based on fault type (Byte 2) 2811 // Type Fan=> Group: 0=FanGroupID, FF-not used 2812 // Byte 5-11 00h, not used 2813 // Byte12 FanLedState [7:0]-Fans 7:0 2814 // Type Memory=> Group: 0 = DIMM GroupID, FF-not used 2815 // Byte 5:12 - DIMM LED state (64bit field, LS Byte first) 2816 // [63:48] = CPU4 channels 7:0, 2 bits per channel 2817 // [47:32] = CPU3 channels 7:0, 2 bits per channel 2818 // [31:16] = CPU2 channels 7:0, 2 bits per channel 2819 // [15:0] = CPU1 channels 7:0, 2 bits per channel 2820 // Type Other=> Component Fault LED Group ID, not used set to 0xFF 2821 // Byte[5:12]: reserved 0x00h 2822 if ((sourceId >= maxFaultSource) || 2823 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) || 2824 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) || 2825 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup))) 2826 { 2827 return ipmi::responseParmOutOfRange(); 2828 } 2829 2830 size_t pinGroupOffset = 0; 2831 size_t pinGroupMax = pinSize / groupSize; 2832 if (RemoteFaultType::fan == RemoteFaultType(faultType)) 2833 { 2834 pinGroupOffset = 4; 2835 pinGroupMax = groupNum - pinSize / groupSize; 2836 } 2837 2838 switch (RemoteFaultType(faultType)) 2839 { 2840 case (RemoteFaultType::fan): 2841 case (RemoteFaultType::memory): 2842 { 2843 if (faultGroup == skipLEDs) 2844 { 2845 return ipmi::responseSuccess(); 2846 } 2847 // calculate led state bit filed count, each byte has 8bits 2848 // the maximum bits will be 8 * 8 bits 2849 constexpr uint8_t size = sizeof(ledStateData) * 8; 2850 2851 // assemble ledState 2852 uint64_t ledState = 0; 2853 bool hasError = false; 2854 for (int i = 0; i < sizeof(ledStateData); i++) 2855 { 2856 ledState = (uint64_t)(ledState << 8); 2857 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]); 2858 } 2859 std::bitset<size> ledStateBits(ledState); 2860 2861 for (int group = 0; group < pinGroupMax; group++) 2862 { 2863 for (int i = 0; i < groupSize; i++) 2864 { // skip non-existing pins 2865 if (0 == faultLedPinNames[group + pinGroupOffset][i].size()) 2866 { 2867 continue; 2868 } 2869 2870 gpiod::line line = gpiod::find_line( 2871 faultLedPinNames[group + pinGroupOffset][i]); 2872 if (!line) 2873 { 2874 phosphor::logging::log<phosphor::logging::level::ERR>( 2875 "Not Find Led Gpio Device!", 2876 phosphor::logging::entry( 2877 "DEVICE=%s", 2878 faultLedPinNames[group + pinGroupOffset][i] 2879 .c_str())); 2880 hasError = true; 2881 continue; 2882 } 2883 2884 bool activeHigh = 2885 (line.active_state() == gpiod::line::ACTIVE_HIGH); 2886 try 2887 { 2888 line.request( 2889 {"faultLed", gpiod::line_request::DIRECTION_OUTPUT, 2890 activeHigh 2891 ? 0 2892 : gpiod::line_request::FLAG_ACTIVE_LOW}); 2893 line.set_value(ledStateBits[i + group * groupSize]); 2894 } 2895 catch (const std::system_error&) 2896 { 2897 phosphor::logging::log<phosphor::logging::level::ERR>( 2898 "Error write Led Gpio Device!", 2899 phosphor::logging::entry( 2900 "DEVICE=%s", 2901 faultLedPinNames[group + pinGroupOffset][i] 2902 .c_str())); 2903 hasError = true; 2904 continue; 2905 } 2906 } // for int i 2907 } 2908 if (hasError) 2909 { 2910 return ipmi::responseResponseError(); 2911 } 2912 break; 2913 } 2914 default: 2915 { 2916 // now only support two fault types 2917 return ipmi::responseParmOutOfRange(); 2918 } 2919 } // switch 2920 return ipmi::responseSuccess(); 2921 } 2922 2923 ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId() 2924 { 2925 uint8_t prodId = 0; 2926 try 2927 { 2928 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 2929 const DbusObjectInfo& object = getDbusObject( 2930 *dbus, "xyz.openbmc_project.Inventory.Item.Board", 2931 "/xyz/openbmc_project/inventory/system/board/", "Baseboard"); 2932 const Value& propValue = getDbusProperty( 2933 *dbus, object.second, object.first, 2934 "xyz.openbmc_project.Inventory.Item.Board.Motherboard", 2935 "ProductId"); 2936 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue)); 2937 } 2938 catch (const std::exception& e) 2939 { 2940 phosphor::logging::log<phosphor::logging::level::ERR>( 2941 "ipmiOEMReadBoardProductId: Product ID read failed!", 2942 phosphor::logging::entry("ERR=%s", e.what())); 2943 } 2944 return ipmi::responseSuccess(prodId); 2945 } 2946 2947 /** @brief implements the get security mode command 2948 * @param ctx - ctx pointer 2949 * 2950 * @returns IPMI completion code with following data 2951 * - restriction mode value - As specified in 2952 * xyz.openbmc_project.Control.Security.RestrictionMode.interface.yaml 2953 * - special mode value - As specified in 2954 * xyz.openbmc_project.Control.Security.SpecialMode.interface.yaml 2955 */ 2956 ipmi::RspType<uint8_t, uint8_t> ipmiGetSecurityMode(ipmi::Context::ptr ctx) 2957 { 2958 namespace securityNameSpace = 2959 sdbusplus::xyz::openbmc_project::Control::Security::server; 2960 uint8_t restrictionModeValue = 0; 2961 uint8_t specialModeValue = 0; 2962 2963 boost::system::error_code ec; 2964 auto varRestrMode = ctx->bus->yield_method_call<ipmi::DbusVariant>( 2965 ctx->yield, ec, restricionModeService, restricionModeBasePath, 2966 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf, 2967 restricionModeProperty); 2968 if (ec) 2969 { 2970 phosphor::logging::log<phosphor::logging::level::ERR>( 2971 "ipmiGetSecurityMode: failed to get RestrictionMode property", 2972 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 2973 return ipmi::responseUnspecifiedError(); 2974 } 2975 restrictionModeValue = static_cast<uint8_t>( 2976 securityNameSpace::RestrictionMode::convertModesFromString( 2977 std::get<std::string>(varRestrMode))); 2978 auto varSpecialMode = ctx->bus->yield_method_call<ipmi::DbusVariant>( 2979 ctx->yield, ec, specialModeService, specialModeBasePath, 2980 dBusPropertyIntf, dBusPropertyGetMethod, specialModeIntf, 2981 specialModeProperty); 2982 if (ec) 2983 { 2984 phosphor::logging::log<phosphor::logging::level::ERR>( 2985 "ipmiGetSecurityMode: failed to get SpecialMode property", 2986 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 2987 // fall through, let us not worry about SpecialMode property, which is 2988 // not required in user scenario 2989 } 2990 else 2991 { 2992 specialModeValue = static_cast<uint8_t>( 2993 securityNameSpace::SpecialMode::convertModesFromString( 2994 std::get<std::string>(varSpecialMode))); 2995 } 2996 return ipmi::responseSuccess(restrictionModeValue, specialModeValue); 2997 } 2998 2999 /** @brief implements the set security mode command 3000 * Command allows to upgrade the restriction mode and won't allow 3001 * to downgrade from system interface 3002 * @param ctx - ctx pointer 3003 * @param restrictionMode - restriction mode value to be set. 3004 * 3005 * @returns IPMI completion code 3006 */ 3007 ipmi::RspType<> ipmiSetSecurityMode(ipmi::Context::ptr ctx, 3008 uint8_t restrictionMode, 3009 std::optional<uint8_t> specialMode) 3010 { 3011 #ifndef BMC_VALIDATION_UNSECURE_FEATURE 3012 if (specialMode) 3013 { 3014 return ipmi::responseReqDataLenInvalid(); 3015 } 3016 #endif 3017 namespace securityNameSpace = 3018 sdbusplus::xyz::openbmc_project::Control::Security::server; 3019 3020 ChannelInfo chInfo; 3021 if (getChannelInfo(ctx->channel, chInfo) != ccSuccess) 3022 { 3023 phosphor::logging::log<phosphor::logging::level::ERR>( 3024 "ipmiSetSecurityMode: Failed to get Channel Info", 3025 phosphor::logging::entry("CHANNEL=%d", ctx->channel)); 3026 return ipmi::responseUnspecifiedError(); 3027 } 3028 auto reqMode = 3029 static_cast<securityNameSpace::RestrictionMode::Modes>(restrictionMode); 3030 3031 if ((reqMode < securityNameSpace::RestrictionMode::Modes::Provisioning) || 3032 (reqMode > 3033 securityNameSpace::RestrictionMode::Modes::ProvisionedHostDisabled)) 3034 { 3035 return ipmi::responseInvalidFieldRequest(); 3036 } 3037 3038 boost::system::error_code ec; 3039 auto varRestrMode = ctx->bus->yield_method_call<ipmi::DbusVariant>( 3040 ctx->yield, ec, restricionModeService, restricionModeBasePath, 3041 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf, 3042 restricionModeProperty); 3043 if (ec) 3044 { 3045 phosphor::logging::log<phosphor::logging::level::ERR>( 3046 "ipmiSetSecurityMode: failed to get RestrictionMode property", 3047 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 3048 return ipmi::responseUnspecifiedError(); 3049 } 3050 auto currentRestrictionMode = 3051 securityNameSpace::RestrictionMode::convertModesFromString( 3052 std::get<std::string>(varRestrMode)); 3053 3054 if (chInfo.mediumType != 3055 static_cast<uint8_t>(EChannelMediumType::lan8032) && 3056 currentRestrictionMode > reqMode) 3057 { 3058 phosphor::logging::log<phosphor::logging::level::ERR>( 3059 "ipmiSetSecurityMode - Downgrading security mode not supported " 3060 "through system interface", 3061 phosphor::logging::entry( 3062 "CUR_MODE=%d", static_cast<uint8_t>(currentRestrictionMode)), 3063 phosphor::logging::entry("REQ_MODE=%d", restrictionMode)); 3064 return ipmi::responseCommandNotAvailable(); 3065 } 3066 3067 ec.clear(); 3068 ctx->bus->yield_method_call<>( 3069 ctx->yield, ec, restricionModeService, restricionModeBasePath, 3070 dBusPropertyIntf, dBusPropertySetMethod, restricionModeIntf, 3071 restricionModeProperty, 3072 static_cast<ipmi::DbusVariant>( 3073 securityNameSpace::convertForMessage(reqMode))); 3074 3075 if (ec) 3076 { 3077 phosphor::logging::log<phosphor::logging::level::ERR>( 3078 "ipmiSetSecurityMode: failed to set RestrictionMode property", 3079 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 3080 return ipmi::responseUnspecifiedError(); 3081 } 3082 3083 #ifdef BMC_VALIDATION_UNSECURE_FEATURE 3084 if (specialMode) 3085 { 3086 constexpr uint8_t mfgMode = 0x01; 3087 // Manufacturing mode is reserved. So can't enable this mode. 3088 if (specialMode.value() == mfgMode) 3089 { 3090 phosphor::logging::log<phosphor::logging::level::INFO>( 3091 "ipmiSetSecurityMode: Can't enable Manufacturing mode"); 3092 return ipmi::responseInvalidFieldRequest(); 3093 } 3094 3095 ec.clear(); 3096 ctx->bus->yield_method_call<>( 3097 ctx->yield, ec, specialModeService, specialModeBasePath, 3098 dBusPropertyIntf, dBusPropertySetMethod, specialModeIntf, 3099 specialModeProperty, 3100 static_cast<ipmi::DbusVariant>(securityNameSpace::convertForMessage( 3101 static_cast<securityNameSpace::SpecialMode::Modes>( 3102 specialMode.value())))); 3103 3104 if (ec) 3105 { 3106 phosphor::logging::log<phosphor::logging::level::ERR>( 3107 "ipmiSetSecurityMode: failed to set SpecialMode property", 3108 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 3109 return ipmi::responseUnspecifiedError(); 3110 } 3111 } 3112 #endif 3113 return ipmi::responseSuccess(); 3114 } 3115 3116 ipmi::RspType<uint8_t /* restore status */> 3117 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd) 3118 { 3119 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'}; 3120 3121 if (clr != expClr) 3122 { 3123 return ipmi::responseInvalidFieldRequest(); 3124 } 3125 constexpr uint8_t cmdStatus = 0; 3126 constexpr uint8_t cmdDefaultRestore = 0xaa; 3127 constexpr uint8_t cmdFullRestore = 0xbb; 3128 constexpr uint8_t cmdFormat = 0xcc; 3129 3130 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op"; 3131 3132 switch (cmd) 3133 { 3134 case cmdStatus: 3135 break; 3136 case cmdDefaultRestore: 3137 case cmdFullRestore: 3138 case cmdFormat: 3139 { 3140 // write file to rwfs root 3141 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3 3142 std::ofstream restoreFile(restoreOpFname); 3143 if (!restoreFile) 3144 { 3145 return ipmi::responseUnspecifiedError(); 3146 } 3147 restoreFile << value << "\n"; 3148 3149 phosphor::logging::log<phosphor::logging::level::WARNING>( 3150 "Restore to default will be performed on next BMC boot", 3151 phosphor::logging::entry("ACTION=0x%0X", cmd)); 3152 3153 break; 3154 } 3155 default: 3156 return ipmi::responseInvalidFieldRequest(); 3157 } 3158 3159 constexpr uint8_t restorePending = 0; 3160 constexpr uint8_t restoreComplete = 1; 3161 3162 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname) 3163 ? restorePending 3164 : restoreComplete; 3165 return ipmi::responseSuccess(restoreStatus); 3166 } 3167 3168 ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void) 3169 { 3170 uint8_t bmcSource; 3171 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server; 3172 3173 try 3174 { 3175 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 3176 std::string service = getService(*dbus, oemNmiSourceIntf, 3177 oemNmiSourceObjPath); 3178 Value variant = getDbusProperty(*dbus, service, oemNmiSourceObjPath, 3179 oemNmiSourceIntf, 3180 oemNmiBmcSourceObjPathProp); 3181 3182 switch (nmi::NMISource::convertBMCSourceSignalFromString( 3183 std::get<std::string>(variant))) 3184 { 3185 case nmi::NMISource::BMCSourceSignal::None: 3186 bmcSource = static_cast<uint8_t>(NmiSource::none); 3187 break; 3188 case nmi::NMISource::BMCSourceSignal::FrontPanelButton: 3189 bmcSource = static_cast<uint8_t>(NmiSource::frontPanelButton); 3190 break; 3191 case nmi::NMISource::BMCSourceSignal::Watchdog: 3192 bmcSource = static_cast<uint8_t>(NmiSource::watchdog); 3193 break; 3194 case nmi::NMISource::BMCSourceSignal::ChassisCmd: 3195 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd); 3196 break; 3197 case nmi::NMISource::BMCSourceSignal::MemoryError: 3198 bmcSource = static_cast<uint8_t>(NmiSource::memoryError); 3199 break; 3200 case nmi::NMISource::BMCSourceSignal::PciBusError: 3201 bmcSource = static_cast<uint8_t>(NmiSource::pciBusError); 3202 break; 3203 case nmi::NMISource::BMCSourceSignal::PCH: 3204 bmcSource = static_cast<uint8_t>(NmiSource::pch); 3205 break; 3206 case nmi::NMISource::BMCSourceSignal::Chipset: 3207 bmcSource = static_cast<uint8_t>(NmiSource::chipset); 3208 break; 3209 default: 3210 phosphor::logging::log<phosphor::logging::level::ERR>( 3211 "NMI source: invalid property!", 3212 phosphor::logging::entry( 3213 "PROP=%s", std::get<std::string>(variant).c_str())); 3214 return ipmi::responseResponseError(); 3215 } 3216 } 3217 catch (const sdbusplus::exception_t& e) 3218 { 3219 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 3220 return ipmi::responseResponseError(); 3221 } 3222 3223 return ipmi::responseSuccess(bmcSource); 3224 } 3225 3226 ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId) 3227 { 3228 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server; 3229 3230 nmi::NMISource::BMCSourceSignal bmcSourceSignal = 3231 nmi::NMISource::BMCSourceSignal::None; 3232 3233 switch (NmiSource(sourceId)) 3234 { 3235 case NmiSource::none: 3236 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None; 3237 break; 3238 case NmiSource::frontPanelButton: 3239 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FrontPanelButton; 3240 break; 3241 case NmiSource::watchdog: 3242 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Watchdog; 3243 break; 3244 case NmiSource::chassisCmd: 3245 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd; 3246 break; 3247 case NmiSource::memoryError: 3248 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError; 3249 break; 3250 case NmiSource::pciBusError: 3251 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciBusError; 3252 break; 3253 case NmiSource::pch: 3254 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PCH; 3255 break; 3256 case NmiSource::chipset: 3257 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Chipset; 3258 break; 3259 default: 3260 phosphor::logging::log<phosphor::logging::level::ERR>( 3261 "NMI source: invalid property!"); 3262 return ipmi::responseResponseError(); 3263 } 3264 3265 try 3266 { 3267 // keep NMI signal source 3268 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 3269 std::string service = getService(*dbus, oemNmiSourceIntf, 3270 oemNmiSourceObjPath); 3271 setDbusProperty(*dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf, 3272 oemNmiBmcSourceObjPathProp, 3273 nmi::convertForMessage(bmcSourceSignal)); 3274 // set Enabled property to inform NMI source handling 3275 // to trigger a NMI_OUT BSOD. 3276 // if it's triggered by NMI source property changed, 3277 // NMI_OUT BSOD could be missed if the same source occurs twice in a row 3278 if (bmcSourceSignal != nmi::NMISource::BMCSourceSignal::None) 3279 { 3280 setDbusProperty(*dbus, service, oemNmiSourceObjPath, 3281 oemNmiSourceIntf, oemNmiEnabledObjPathProp, 3282 static_cast<bool>(true)); 3283 } 3284 } 3285 catch (const sdbusplus::exception_t& e) 3286 { 3287 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 3288 return ipmi::responseResponseError(); 3289 } 3290 3291 return ipmi::responseSuccess(); 3292 } 3293 3294 namespace dimmOffset 3295 { 3296 constexpr const char* dimmPower = "DimmPower"; 3297 constexpr const char* staticCltt = "StaticCltt"; 3298 constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm"; 3299 constexpr const char* offsetInterface = 3300 "xyz.openbmc_project.Inventory.Item.Dimm.Offset"; 3301 constexpr const char* property = "DimmOffset"; 3302 3303 }; // namespace dimmOffset 3304 3305 ipmi::RspType<> 3306 ipmiOEMSetDimmOffset(uint8_t type, 3307 const std::vector<std::tuple<uint8_t, uint8_t>>& data) 3308 { 3309 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) && 3310 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt)) 3311 { 3312 return ipmi::responseInvalidFieldRequest(); 3313 } 3314 3315 if (data.empty()) 3316 { 3317 return ipmi::responseInvalidFieldRequest(); 3318 } 3319 nlohmann::json json; 3320 3321 std::ifstream jsonStream(dimmOffsetFile); 3322 if (jsonStream.good()) 3323 { 3324 json = nlohmann::json::parse(jsonStream, nullptr, false); 3325 if (json.is_discarded()) 3326 { 3327 json = nlohmann::json(); 3328 } 3329 jsonStream.close(); 3330 } 3331 3332 std::string typeName; 3333 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower)) 3334 { 3335 typeName = dimmOffset::dimmPower; 3336 } 3337 else 3338 { 3339 typeName = dimmOffset::staticCltt; 3340 } 3341 3342 nlohmann::json& field = json[typeName]; 3343 3344 for (const auto& [index, value] : data) 3345 { 3346 field[index] = value; 3347 } 3348 3349 for (nlohmann::json& val : field) 3350 { 3351 if (val == nullptr) 3352 { 3353 val = static_cast<uint8_t>(0); 3354 } 3355 } 3356 3357 std::ofstream output(dimmOffsetFile); 3358 if (!output.good()) 3359 { 3360 std::cerr << "Error writing json file\n"; 3361 return ipmi::responseResponseError(); 3362 } 3363 3364 output << json.dump(4); 3365 3366 if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt)) 3367 { 3368 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 3369 3370 ipmi::DbusVariant offsets = field.get<std::vector<uint8_t>>(); 3371 auto call = bus->new_method_call( 3372 settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set"); 3373 call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets); 3374 try 3375 { 3376 bus->call(call); 3377 } 3378 catch (const sdbusplus::exception_t& e) 3379 { 3380 phosphor::logging::log<phosphor::logging::level::ERR>( 3381 "ipmiOEMSetDimmOffset: can't set dimm offsets!", 3382 phosphor::logging::entry("ERR=%s", e.what())); 3383 return ipmi::responseResponseError(); 3384 } 3385 } 3386 3387 return ipmi::responseSuccess(); 3388 } 3389 3390 ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index) 3391 { 3392 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) && 3393 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt)) 3394 { 3395 return ipmi::responseInvalidFieldRequest(); 3396 } 3397 3398 std::ifstream jsonStream(dimmOffsetFile); 3399 3400 auto json = nlohmann::json::parse(jsonStream, nullptr, false); 3401 if (json.is_discarded()) 3402 { 3403 std::cerr << "File error in " << dimmOffsetFile << "\n"; 3404 return ipmi::responseResponseError(); 3405 } 3406 3407 std::string typeName; 3408 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower)) 3409 { 3410 typeName = dimmOffset::dimmPower; 3411 } 3412 else 3413 { 3414 typeName = dimmOffset::staticCltt; 3415 } 3416 3417 auto it = json.find(typeName); 3418 if (it == json.end()) 3419 { 3420 return ipmi::responseInvalidFieldRequest(); 3421 } 3422 3423 if (it->size() <= index) 3424 { 3425 return ipmi::responseInvalidFieldRequest(); 3426 } 3427 3428 uint8_t resp = it->at(index).get<uint8_t>(); 3429 return ipmi::responseSuccess(resp); 3430 } 3431 3432 namespace boot_options 3433 { 3434 3435 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server; 3436 using IpmiValue = uint8_t; 3437 constexpr auto ipmiDefault = 0; 3438 3439 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = { 3440 {0x01, Source::Sources::Network}, 3441 {0x02, Source::Sources::Disk}, 3442 {0x05, Source::Sources::ExternalMedia}, 3443 {0x0f, Source::Sources::RemovableMedia}, 3444 {ipmiDefault, Source::Sources::Default}}; 3445 3446 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { 3447 {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}}; 3448 3449 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = { 3450 {Source::Sources::Network, 0x01}, 3451 {Source::Sources::Disk, 0x02}, 3452 {Source::Sources::ExternalMedia, 0x05}, 3453 {Source::Sources::RemovableMedia, 0x0f}, 3454 {Source::Sources::Default, ipmiDefault}}; 3455 3456 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = { 3457 {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}}; 3458 3459 static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode"; 3460 static constexpr auto bootSourceIntf = 3461 "xyz.openbmc_project.Control.Boot.Source"; 3462 static constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable"; 3463 static constexpr auto persistentObjPath = 3464 "/xyz/openbmc_project/control/host0/boot"; 3465 static constexpr auto oneTimePath = 3466 "/xyz/openbmc_project/control/host0/boot/one_time"; 3467 static constexpr auto bootSourceProp = "BootSource"; 3468 static constexpr auto bootModeProp = "BootMode"; 3469 static constexpr auto oneTimeBootEnableProp = "Enabled"; 3470 static constexpr auto httpBootMode = 3471 "xyz.openbmc_project.Control.Boot.Source.Sources.Http"; 3472 3473 enum class BootOptionParameter : size_t 3474 { 3475 setInProgress = 0x0, 3476 bootFlags = 0x5, 3477 }; 3478 static constexpr uint8_t setComplete = 0x0; 3479 static constexpr uint8_t setInProgress = 0x1; 3480 static uint8_t transferStatus = setComplete; 3481 static constexpr uint8_t setParmVersion = 0x01; 3482 static constexpr uint8_t setParmBootFlagsPermanent = 0x40; 3483 static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80; 3484 static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0; 3485 static constexpr uint8_t httpBoot = 0xd; 3486 static constexpr uint8_t bootSourceMask = 0x3c; 3487 3488 } // namespace boot_options 3489 3490 ipmi::RspType<uint8_t, // version 3491 uint8_t, // param 3492 uint8_t, // data0, dependent on parameter 3493 std::optional<uint8_t> // data1, dependent on parameter 3494 > 3495 ipmiOemGetEfiBootOptions(uint8_t parameter, uint8_t set, uint8_t block) 3496 { 3497 using namespace boot_options; 3498 uint8_t bootOption = 0; 3499 3500 if (parameter == static_cast<uint8_t>(BootOptionParameter::setInProgress)) 3501 { 3502 return ipmi::responseSuccess(setParmVersion, parameter, transferStatus, 3503 std::nullopt); 3504 } 3505 3506 if (parameter != static_cast<uint8_t>(BootOptionParameter::bootFlags)) 3507 { 3508 phosphor::logging::log<phosphor::logging::level::ERR>( 3509 "Unsupported parameter"); 3510 return ipmi::response(ccParameterNotSupported); 3511 } 3512 3513 try 3514 { 3515 auto oneTimeEnabled = false; 3516 // read one time Enabled property 3517 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 3518 std::string service = getService(*dbus, enabledIntf, oneTimePath); 3519 Value variant = getDbusProperty(*dbus, service, oneTimePath, 3520 enabledIntf, oneTimeBootEnableProp); 3521 oneTimeEnabled = std::get<bool>(variant); 3522 3523 // get BootSource and BootMode properties 3524 // according to oneTimeEnable 3525 auto bootObjPath = oneTimePath; 3526 if (oneTimeEnabled == false) 3527 { 3528 bootObjPath = persistentObjPath; 3529 } 3530 3531 service = getService(*dbus, bootModeIntf, bootObjPath); 3532 variant = getDbusProperty(*dbus, service, bootObjPath, bootModeIntf, 3533 bootModeProp); 3534 3535 auto bootMode = 3536 Mode::convertModesFromString(std::get<std::string>(variant)); 3537 3538 service = getService(*dbus, bootSourceIntf, bootObjPath); 3539 variant = getDbusProperty(*dbus, service, bootObjPath, bootSourceIntf, 3540 bootSourceProp); 3541 3542 if (std::get<std::string>(variant) == httpBootMode) 3543 { 3544 bootOption = httpBoot; 3545 } 3546 else 3547 { 3548 auto bootSource = Source::convertSourcesFromString( 3549 std::get<std::string>(variant)); 3550 bootOption = sourceDbusToIpmi.at(bootSource); 3551 if (Source::Sources::Default == bootSource) 3552 { 3553 bootOption = modeDbusToIpmi.at(bootMode); 3554 } 3555 } 3556 3557 uint8_t oneTime = oneTimeEnabled ? setParmBootFlagsValidOneTime 3558 : setParmBootFlagsValidPermanent; 3559 bootOption <<= 2; // shift for responseconstexpr 3560 return ipmi::responseSuccess(setParmVersion, parameter, oneTime, 3561 bootOption); 3562 } 3563 catch (const sdbusplus::exception_t& e) 3564 { 3565 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 3566 return ipmi::responseResponseError(); 3567 } 3568 } 3569 3570 ipmi::RspType<> ipmiOemSetEfiBootOptions(uint8_t bootFlag, uint8_t bootParam, 3571 std::optional<uint8_t> bootOption) 3572 { 3573 using namespace boot_options; 3574 auto oneTimeEnabled = false; 3575 3576 if (bootFlag == 0 && bootParam == 0) 3577 { 3578 phosphor::logging::log<phosphor::logging::level::ERR>( 3579 "Unsupported parameter"); 3580 return ipmi::response(ccParameterNotSupported); 3581 } 3582 if (bootFlag == static_cast<uint8_t>(BootOptionParameter::setInProgress)) 3583 { 3584 if (bootOption) 3585 { 3586 return ipmi::responseReqDataLenInvalid(); 3587 } 3588 3589 if (transferStatus == setInProgress) 3590 { 3591 phosphor::logging::log<phosphor::logging::level::ERR>( 3592 "boot option set in progress!"); 3593 return ipmi::responseResponseError(); 3594 } 3595 3596 transferStatus = bootParam; 3597 return ipmi::responseSuccess(); 3598 } 3599 3600 if (bootFlag != (uint8_t)BootOptionParameter::bootFlags) 3601 { 3602 phosphor::logging::log<phosphor::logging::level::ERR>( 3603 "Unsupported parameter"); 3604 return ipmi::response(ccParameterNotSupported); 3605 } 3606 3607 if (!bootOption) 3608 { 3609 return ipmi::responseReqDataLenInvalid(); 3610 } 3611 3612 if (((bootOption.value() & bootSourceMask) >> 2) != 3613 httpBoot) // not http boot, exit 3614 { 3615 phosphor::logging::log<phosphor::logging::level::ERR>( 3616 "wrong boot option parameter!"); 3617 return ipmi::responseParmOutOfRange(); 3618 } 3619 3620 try 3621 { 3622 bool permanent = (bootParam & setParmBootFlagsPermanent) == 3623 setParmBootFlagsPermanent; 3624 3625 // read one time Enabled property 3626 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 3627 std::string service = getService(*dbus, enabledIntf, oneTimePath); 3628 Value variant = getDbusProperty(*dbus, service, oneTimePath, 3629 enabledIntf, oneTimeBootEnableProp); 3630 oneTimeEnabled = std::get<bool>(variant); 3631 3632 /* 3633 * Check if the current boot setting is onetime or permanent, if the 3634 * request in the command is otherwise, then set the "Enabled" 3635 * property in one_time object path to 'True' to indicate onetime 3636 * and 'False' to indicate permanent. 3637 * 3638 * Once the onetime/permanent setting is applied, then the bootMode 3639 * and bootSource is updated for the corresponding object. 3640 */ 3641 if (permanent == oneTimeEnabled) 3642 { 3643 setDbusProperty(*dbus, service, oneTimePath, enabledIntf, 3644 oneTimeBootEnableProp, !permanent); 3645 } 3646 3647 // set BootSource and BootMode properties 3648 // according to oneTimeEnable or persistent 3649 auto bootObjPath = oneTimePath; 3650 if (oneTimeEnabled == false) 3651 { 3652 bootObjPath = persistentObjPath; 3653 } 3654 std::string bootMode = 3655 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"; 3656 std::string bootSource = httpBootMode; 3657 3658 service = getService(*dbus, bootModeIntf, bootObjPath); 3659 setDbusProperty(*dbus, service, bootObjPath, bootModeIntf, bootModeProp, 3660 bootMode); 3661 3662 service = getService(*dbus, bootSourceIntf, bootObjPath); 3663 setDbusProperty(*dbus, service, bootObjPath, bootSourceIntf, 3664 bootSourceProp, bootSource); 3665 } 3666 catch (const sdbusplus::exception_t& e) 3667 { 3668 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 3669 return ipmi::responseResponseError(); 3670 } 3671 3672 return ipmi::responseSuccess(); 3673 } 3674 3675 using BasicVariantType = ipmi::DbusVariant; 3676 using PropertyMapType = 3677 boost::container::flat_map<std::string, BasicVariantType>; 3678 static constexpr const std::array<const char*, 1> psuPresenceTypes = { 3679 "xyz.openbmc_project.Configuration.PSUPresence"}; 3680 int getPSUAddress(ipmi::Context::ptr ctx, uint8_t& bus, 3681 std::vector<uint64_t>& addrTable) 3682 { 3683 boost::system::error_code ec; 3684 GetSubTreeType subtree = ctx->bus->yield_method_call<GetSubTreeType>( 3685 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper", 3686 "/xyz/openbmc_project/object_mapper", 3687 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 3688 "/xyz/openbmc_project/inventory/system", 3, psuPresenceTypes); 3689 if (ec) 3690 { 3691 phosphor::logging::log<phosphor::logging::level::ERR>( 3692 "Failed to set dbus property to cold redundancy"); 3693 return -1; 3694 } 3695 for (const auto& object : subtree) 3696 { 3697 std::string pathName = object.first; 3698 for (const auto& serviceIface : object.second) 3699 { 3700 std::string serviceName = serviceIface.first; 3701 3702 ec.clear(); 3703 PropertyMapType propMap = 3704 ctx->bus->yield_method_call<PropertyMapType>( 3705 ctx->yield, ec, serviceName, pathName, 3706 "org.freedesktop.DBus.Properties", "GetAll", 3707 "xyz.openbmc_project.Configuration.PSUPresence"); 3708 if (ec) 3709 { 3710 phosphor::logging::log<phosphor::logging::level::ERR>( 3711 "Failed to set dbus property to cold redundancy"); 3712 return -1; 3713 } 3714 auto psuBus = std::get_if<uint64_t>(&propMap["Bus"]); 3715 auto psuAddress = 3716 std::get_if<std::vector<uint64_t>>(&propMap["Address"]); 3717 3718 if (psuBus == nullptr || psuAddress == nullptr) 3719 { 3720 std::cerr << "error finding necessary " 3721 "entry in configuration\n"; 3722 return -1; 3723 } 3724 bus = static_cast<uint8_t>(*psuBus); 3725 addrTable = *psuAddress; 3726 return 0; 3727 } 3728 } 3729 return -1; 3730 } 3731 3732 static const constexpr uint8_t addrOffset = 8; 3733 static const constexpr uint8_t psuRevision = 0xd9; 3734 static const constexpr uint8_t defaultPSUBus = 7; 3735 // Second Minor, Primary Minor, Major 3736 static const constexpr size_t verLen = 3; 3737 ipmi::RspType<std::vector<uint8_t>> ipmiOEMGetPSUVersion(ipmi::Context::ptr ctx) 3738 { 3739 uint8_t bus = defaultPSUBus; 3740 std::vector<uint64_t> addrTable; 3741 std::vector<uint8_t> result; 3742 if (getPSUAddress(ctx, bus, addrTable)) 3743 { 3744 std::cerr << "Failed to get PSU bus and address\n"; 3745 return ipmi::responseResponseError(); 3746 } 3747 3748 for (const auto& targetAddr : addrTable) 3749 { 3750 std::vector<uint8_t> writeData = {psuRevision}; 3751 std::vector<uint8_t> readBuf(verLen); 3752 uint8_t addr = static_cast<uint8_t>(targetAddr) + addrOffset; 3753 std::string i2cBus = "/dev/i2c-" + std::to_string(bus); 3754 3755 auto retI2C = ipmi::i2cWriteRead(i2cBus, addr, writeData, readBuf); 3756 if (retI2C != ipmi::ccSuccess) 3757 { 3758 for (size_t idx = 0; idx < verLen; idx++) 3759 { 3760 result.emplace_back(0x00); 3761 } 3762 } 3763 else 3764 { 3765 for (const uint8_t& data : readBuf) 3766 { 3767 result.emplace_back(data); 3768 } 3769 } 3770 } 3771 3772 return ipmi::responseSuccess(result); 3773 } 3774 3775 std::optional<uint8_t> getMultiNodeInfoPresence(ipmi::Context::ptr ctx, 3776 const std::string& name) 3777 { 3778 Value dbusValue = 0; 3779 std::string serviceName; 3780 3781 boost::system::error_code ec = 3782 ipmi::getService(ctx, multiNodeIntf, multiNodeObjPath, serviceName); 3783 3784 if (ec) 3785 { 3786 phosphor::logging::log<phosphor::logging::level::ERR>( 3787 "Failed to perform Multinode getService."); 3788 return std::nullopt; 3789 } 3790 3791 ec = ipmi::getDbusProperty(ctx, serviceName, multiNodeObjPath, 3792 multiNodeIntf, name, dbusValue); 3793 if (ec) 3794 { 3795 phosphor::logging::log<phosphor::logging::level::ERR>( 3796 "Failed to perform Multinode get property"); 3797 return std::nullopt; 3798 } 3799 3800 auto multiNodeVal = std::get_if<uint8_t>(&dbusValue); 3801 if (!multiNodeVal) 3802 { 3803 phosphor::logging::log<phosphor::logging::level::ERR>( 3804 "getMultiNodeInfoPresence: error to get multinode"); 3805 return std::nullopt; 3806 } 3807 return *multiNodeVal; 3808 } 3809 3810 /** @brief implements OEM get reading command 3811 * @param domain ID 3812 * @param reading Type 3813 * - 00h = platform Power Consumption 3814 * - 01h = inlet Air Temp 3815 * - 02h = icc_TDC from PECI 3816 * @param reserved, write as 0000h 3817 * 3818 * @returns IPMI completion code plus response data 3819 * - response 3820 * - domain ID 3821 * - reading Type 3822 * - 00h = platform Power Consumption 3823 * - 01h = inlet Air Temp 3824 * - 02h = icc_TDC from PECI 3825 * - reading 3826 */ 3827 ipmi::RspType<uint4_t, // domain ID 3828 uint4_t, // reading Type 3829 uint16_t // reading Value 3830 > 3831 ipmiOEMGetReading(ipmi::Context::ptr ctx, uint4_t domainId, 3832 uint4_t readingType, uint16_t reserved) 3833 { 3834 constexpr uint8_t platformPower = 0; 3835 constexpr uint8_t inletAirTemp = 1; 3836 constexpr uint8_t iccTdc = 2; 3837 3838 if ((static_cast<uint8_t>(readingType) > iccTdc) || domainId || reserved) 3839 { 3840 return ipmi::responseInvalidFieldRequest(); 3841 } 3842 3843 // This command should run only from multi-node product. 3844 // For all other platforms this command will return invalid. 3845 3846 std::optional<uint8_t> nodeInfo = getMultiNodeInfoPresence(ctx, 3847 "NodePresence"); 3848 if (!nodeInfo || !*nodeInfo) 3849 { 3850 return ipmi::responseInvalidCommand(); 3851 } 3852 3853 uint16_t oemReadingValue = 0; 3854 if (static_cast<uint8_t>(readingType) == inletAirTemp) 3855 { 3856 double value = 0; 3857 boost::system::error_code ec = ipmi::getDbusProperty( 3858 ctx, "xyz.openbmc_project.HwmonTempSensor", 3859 "/xyz/openbmc_project/sensors/temperature/Inlet_BRD_Temp", 3860 "xyz.openbmc_project.Sensor.Value", "Value", value); 3861 if (ec) 3862 { 3863 phosphor::logging::log<phosphor::logging::level::ERR>( 3864 "Failed to get BMC Get OEM temperature", 3865 phosphor::logging::entry("EXCEPTION=%s", ec.message().c_str())); 3866 return ipmi::responseUnspecifiedError(); 3867 } 3868 // Take the Inlet temperature 3869 oemReadingValue = static_cast<uint16_t>(value); 3870 } 3871 else 3872 { 3873 phosphor::logging::log<phosphor::logging::level::ERR>( 3874 "Currently Get OEM Reading support only for Inlet Air Temp"); 3875 return ipmi::responseParmOutOfRange(); 3876 } 3877 return ipmi::responseSuccess(domainId, readingType, oemReadingValue); 3878 } 3879 3880 /** @brief implements the maximum size of 3881 * bridgeable messages used between KCS and 3882 * IPMB interfacesget security mode command. 3883 * 3884 * @returns IPMI completion code with following data 3885 * - KCS Buffer Size (In multiples of four bytes) 3886 * - IPMB Buffer Size (In multiples of four bytes) 3887 **/ 3888 ipmi::RspType<uint8_t, uint8_t> ipmiOEMGetBufferSize() 3889 { 3890 // for now this is hard coded; really this number is dependent on 3891 // the BMC kcs driver as well as the host kcs driver.... 3892 // we can't know the latter. 3893 uint8_t kcsMaxBufferSize = 63 / 4; 3894 uint8_t ipmbMaxBufferSize = 128 / 4; 3895 3896 return ipmi::responseSuccess(kcsMaxBufferSize, ipmbMaxBufferSize); 3897 } 3898 3899 ipmi::RspType<std::vector<uint8_t>> 3900 ipmiOEMReadPFRMailbox(ipmi::Context::ptr& ctx, const uint8_t readRegister, 3901 const uint8_t numOfBytes, uint8_t registerIdentifier) 3902 { 3903 if (!ipmi::mailbox::i2cConfigLoaded) 3904 { 3905 phosphor::logging::log<phosphor::logging::level::ERR>( 3906 "Calling PFR Load Configuration Function to Get I2C Bus and Target " 3907 "Address "); 3908 3909 ipmi::mailbox::loadPfrConfig(ctx, ipmi::mailbox::i2cConfigLoaded); 3910 } 3911 3912 if (!numOfBytes && !readRegister) 3913 { 3914 phosphor::logging::log<phosphor::logging::level::ERR>( 3915 "OEM IPMI command: Read & write count are 0 which is invalid "); 3916 return ipmi::responseInvalidFieldRequest(); 3917 } 3918 3919 switch (registerIdentifier) 3920 { 3921 case ipmi::mailbox::registerType::fifoReadRegister: 3922 { 3923 // Check if readRegister is an FIFO read register 3924 if (registerIdentifier == 1) 3925 { 3926 if (ipmi::mailbox::readFifoReg.find(readRegister) == 3927 ipmi::mailbox::readFifoReg.end()) 3928 { 3929 phosphor::logging::log<phosphor::logging::level::ERR>( 3930 "OEM IPMI command: Register is not a Read FIFO "); 3931 return ipmi::responseInvalidFieldRequest(); 3932 } 3933 3934 phosphor::logging::log<phosphor::logging::level::ERR>( 3935 "OEM IPMI command: Register is a Read FIFO "); 3936 3937 ipmi::mailbox::writefifo(ipmi::mailbox::provisioningCommand, 3938 readRegister); 3939 ipmi::mailbox::writefifo(ipmi::mailbox::triggerCommand, 3940 ipmi::mailbox::flushRead); 3941 3942 std::vector<uint8_t> writeData = {ipmi::mailbox::readFifo}; 3943 std::vector<uint8_t> readBuf(1); 3944 std::vector<uint8_t> result; 3945 3946 for (int i = 0; i < numOfBytes; i++) 3947 { 3948 ipmi::Cc ret = ipmi::i2cWriteRead(ipmi::mailbox::i2cBus, 3949 ipmi::mailbox::targetAddr, 3950 writeData, readBuf); 3951 if (ret != ipmi::ccSuccess) 3952 { 3953 return ipmi::response(ret); 3954 } 3955 3956 else 3957 { 3958 for (const uint8_t& data : readBuf) 3959 { 3960 result.emplace_back(data); 3961 } 3962 } 3963 } 3964 3965 return ipmi::responseSuccess(result); 3966 } 3967 } 3968 3969 case ipmi::mailbox::registerType::singleByteRegister: 3970 { 3971 phosphor::logging::log<phosphor::logging::level::ERR>( 3972 "OEM IPMI command: Register is a Single Byte Register "); 3973 3974 std::vector<uint8_t> writeData = {readRegister}; 3975 std::vector<uint8_t> readBuf(numOfBytes); 3976 3977 ipmi::Cc ret = ipmi::i2cWriteRead(ipmi::mailbox::i2cBus, 3978 ipmi::mailbox::targetAddr, 3979 writeData, readBuf); 3980 if (ret != ipmi::ccSuccess) 3981 { 3982 return ipmi::response(ret); 3983 } 3984 return ipmi::responseSuccess(readBuf); 3985 } 3986 3987 default: 3988 { 3989 phosphor::logging::log<phosphor::logging::level::ERR>( 3990 "OEM IPMI command: Register identifier is not valid.It should " 3991 "be 0 " 3992 "for Single Byte Register and 1 for FIFO Read Register"); 3993 3994 return ipmi::responseInvalidFieldRequest(); 3995 } 3996 } 3997 } 3998 3999 static void registerOEMFunctions(void) 4000 { 4001 phosphor::logging::log<phosphor::logging::level::INFO>( 4002 "Registering OEM commands"); 4003 registerHandler(prioOemBase, intel::netFnGeneral, 4004 intel::general::cmdGetBmcVersionString, Privilege::User, 4005 ipmiOEMGetBmcVersionString); 4006 4007 ipmiPrintAndRegister(intel::netFnGeneral, 4008 intel::general::cmdGetChassisIdentifier, NULL, 4009 ipmiOEMGetChassisIdentifier, 4010 PRIVILEGE_USER); // get chassis identifier 4011 4012 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetSystemGUID, 4013 NULL, ipmiOEMSetSystemGUID, 4014 PRIVILEGE_ADMIN); // set system guid 4015 4016 // <Disable BMC System Reset Action> 4017 registerHandler(prioOemBase, intel::netFnGeneral, 4018 intel::general::cmdDisableBMCSystemReset, Privilege::Admin, 4019 ipmiOEMDisableBMCSystemReset); 4020 4021 // <Get BMC Reset Disables> 4022 registerHandler(prioOemBase, intel::netFnGeneral, 4023 intel::general::cmdGetBMCResetDisables, Privilege::Admin, 4024 ipmiOEMGetBMCResetDisables); 4025 4026 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetBIOSID, 4027 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN); 4028 4029 registerHandler(prioOemBase, intel::netFnGeneral, 4030 intel::general::cmdGetOEMDeviceInfo, Privilege::User, 4031 ipmiOEMGetDeviceInfo); 4032 4033 ipmiPrintAndRegister(intel::netFnGeneral, 4034 intel::general::cmdGetAICSlotFRUIDSlotPosRecords, NULL, 4035 ipmiOEMGetAICFRU, PRIVILEGE_USER); 4036 4037 registerHandler(prioOpenBmcBase, intel::netFnGeneral, 4038 intel::general::cmdSendEmbeddedFWUpdStatus, 4039 Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus); 4040 4041 registerHandler(prioOpenBmcBase, intel::netFnApp, intel::app::cmdSlotIpmb, 4042 Privilege::Admin, ipmiOEMSlotIpmb); 4043 4044 ipmiPrintAndRegister(intel::netFnGeneral, 4045 intel::general::cmdSetPowerRestoreDelay, NULL, 4046 ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR); 4047 4048 ipmiPrintAndRegister(intel::netFnGeneral, 4049 intel::general::cmdGetPowerRestoreDelay, NULL, 4050 ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER); 4051 4052 registerHandler(prioOpenBmcBase, intel::netFnGeneral, 4053 intel::general::cmdSetOEMUser2Activation, 4054 Privilege::Callback, ipmiOEMSetUser2Activation); 4055 4056 registerHandler(prioOpenBmcBase, intel::netFnGeneral, 4057 intel::general::cmdSetSpecialUserPassword, 4058 Privilege::Callback, ipmiOEMSetSpecialUserPassword); 4059 4060 // <Get Processor Error Config> 4061 registerHandler(prioOemBase, intel::netFnGeneral, 4062 intel::general::cmdGetProcessorErrConfig, Privilege::User, 4063 ipmiOEMGetProcessorErrConfig); 4064 4065 // <Set Processor Error Config> 4066 registerHandler(prioOemBase, intel::netFnGeneral, 4067 intel::general::cmdSetProcessorErrConfig, Privilege::Admin, 4068 ipmiOEMSetProcessorErrConfig); 4069 4070 ipmiPrintAndRegister(intel::netFnGeneral, 4071 intel::general::cmdSetShutdownPolicy, NULL, 4072 ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN); 4073 4074 ipmiPrintAndRegister(intel::netFnGeneral, 4075 intel::general::cmdGetShutdownPolicy, NULL, 4076 ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN); 4077 4078 registerHandler(prioOemBase, intel::netFnGeneral, 4079 intel::general::cmdSetFanConfig, Privilege::User, 4080 ipmiOEMSetFanConfig); 4081 4082 registerHandler(prioOemBase, intel::netFnGeneral, 4083 intel::general::cmdGetFanConfig, Privilege::User, 4084 ipmiOEMGetFanConfig); 4085 4086 registerHandler(prioOemBase, intel::netFnGeneral, 4087 intel::general::cmdGetFanSpeedOffset, Privilege::User, 4088 ipmiOEMGetFanSpeedOffset); 4089 4090 registerHandler(prioOemBase, intel::netFnGeneral, 4091 intel::general::cmdSetFanSpeedOffset, Privilege::User, 4092 ipmiOEMSetFanSpeedOffset); 4093 4094 registerHandler(prioOemBase, intel::netFnGeneral, 4095 intel::general::cmdSetFscParameter, Privilege::User, 4096 ipmiOEMSetFscParameter); 4097 4098 registerHandler(prioOemBase, intel::netFnGeneral, 4099 intel::general::cmdGetFscParameter, Privilege::User, 4100 ipmiOEMGetFscParameter); 4101 4102 registerHandler(prioOpenBmcBase, intel::netFnGeneral, 4103 intel::general::cmdReadBaseBoardProductId, Privilege::Admin, 4104 ipmiOEMReadBoardProductId); 4105 4106 registerHandler(prioOemBase, intel::netFnGeneral, 4107 intel::general::cmdGetNmiStatus, Privilege::User, 4108 ipmiOEMGetNmiSource); 4109 4110 registerHandler(prioOemBase, intel::netFnGeneral, 4111 intel::general::cmdSetNmiStatus, Privilege::Operator, 4112 ipmiOEMSetNmiSource); 4113 4114 registerHandler(prioOemBase, intel::netFnGeneral, 4115 intel::general::cmdGetEfiBootOptions, Privilege::User, 4116 ipmiOemGetEfiBootOptions); 4117 4118 registerHandler(prioOemBase, intel::netFnGeneral, 4119 intel::general::cmdSetEfiBootOptions, Privilege::Operator, 4120 ipmiOemSetEfiBootOptions); 4121 4122 registerHandler(prioOemBase, intel::netFnGeneral, 4123 intel::general::cmdGetSecurityMode, Privilege::User, 4124 ipmiGetSecurityMode); 4125 4126 registerHandler(prioOemBase, intel::netFnGeneral, 4127 intel::general::cmdSetSecurityMode, Privilege::Admin, 4128 ipmiSetSecurityMode); 4129 4130 registerHandler(prioOemBase, intel::netFnGeneral, 4131 intel::general::cmdGetLEDStatus, Privilege::Admin, 4132 ipmiOEMGetLEDStatus); 4133 4134 ipmiPrintAndRegister(ipmi::intel::netFnPlatform, 4135 ipmi::intel::platform::cmdCfgHostSerialPortSpeed, NULL, 4136 ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN); 4137 4138 registerHandler(prioOemBase, intel::netFnGeneral, 4139 intel::general::cmdSetFaultIndication, Privilege::Operator, 4140 ipmiOEMSetFaultIndication); 4141 4142 registerHandler(prioOemBase, intel::netFnGeneral, 4143 intel::general::cmdSetColdRedundancyConfig, Privilege::User, 4144 ipmiOEMSetCRConfig); 4145 4146 registerHandler(prioOemBase, intel::netFnGeneral, 4147 intel::general::cmdGetColdRedundancyConfig, Privilege::User, 4148 ipmiOEMGetCRConfig); 4149 4150 registerHandler(prioOemBase, intel::netFnGeneral, 4151 intel::general::cmdRestoreConfiguration, Privilege::Admin, 4152 ipmiRestoreConfiguration); 4153 4154 registerHandler(prioOemBase, intel::netFnGeneral, 4155 intel::general::cmdSetDimmOffset, Privilege::Operator, 4156 ipmiOEMSetDimmOffset); 4157 4158 registerHandler(prioOemBase, intel::netFnGeneral, 4159 intel::general::cmdGetDimmOffset, Privilege::Operator, 4160 ipmiOEMGetDimmOffset); 4161 4162 registerHandler(prioOemBase, intel::netFnGeneral, 4163 intel::general::cmdGetPSUVersion, Privilege::User, 4164 ipmiOEMGetPSUVersion); 4165 4166 registerHandler(prioOemBase, intel::netFnGeneral, 4167 intel::general::cmdGetBufferSize, Privilege::User, 4168 ipmiOEMGetBufferSize); 4169 4170 registerHandler(prioOemBase, intel::netFnGeneral, 4171 intel::general::cmdOEMGetReading, Privilege::User, 4172 ipmiOEMGetReading); 4173 4174 registerHandler(prioOemBase, intel::netFnApp, intel::app::cmdPFRMailboxRead, 4175 Privilege::Admin, ipmiOEMReadPFRMailbox); 4176 } 4177 4178 } // namespace ipmi 4179