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