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