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