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