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