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