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