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