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