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