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