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