1 #include "config.h" 2 3 #include "dcmihandler.hpp" 4 5 #include "user_channel/channel_layer.hpp" 6 7 #include <ipmid/api.hpp> 8 #include <ipmid/utils.hpp> 9 #include <nlohmann/json.hpp> 10 #include <phosphor-logging/elog-errors.hpp> 11 #include <phosphor-logging/lg2.hpp> 12 #include <sdbusplus/bus.hpp> 13 #include <xyz/openbmc_project/Common/error.hpp> 14 #include <xyz/openbmc_project/Network/EthernetInterface/server.hpp> 15 16 #include <bitset> 17 #include <cmath> 18 #include <fstream> 19 #include <variant> 20 21 using namespace phosphor::logging; 22 using sdbusplus::server::xyz::openbmc_project::network::EthernetInterface; 23 24 using InternalFailure = 25 sdbusplus::error::xyz::openbmc_project::common::InternalFailure; 26 27 void register_netfn_dcmi_functions() __attribute__((constructor)); 28 29 constexpr auto pcapPath = "/xyz/openbmc_project/control/host0/power_cap"; 30 constexpr auto pcapInterface = "xyz.openbmc_project.Control.Power.Cap"; 31 32 constexpr auto powerCapProp = "PowerCap"; 33 constexpr auto powerCapEnableProp = "PowerCapEnable"; 34 35 using namespace phosphor::logging; 36 37 namespace dcmi 38 { 39 constexpr auto assetTagMaxOffset = 62; 40 constexpr auto assetTagMaxSize = 63; 41 constexpr auto maxBytes = 16; 42 constexpr size_t maxCtrlIdStrLen = 63; 43 44 constexpr uint8_t parameterRevision = 2; 45 constexpr uint8_t specMajorVersion = 1; 46 constexpr uint8_t specMinorVersion = 5; 47 constexpr auto sensorValueIntf = "xyz.openbmc_project.Sensor.Value"; 48 constexpr auto sensorValueProp = "Value"; 49 constexpr uint8_t configParameterRevision = 1; 50 constexpr auto option12Mask = 0x01; 51 constexpr auto activateDhcpReply = 0x00; 52 constexpr uint8_t dhcpTiming1 = 0x04; // 4 sec 53 constexpr uint16_t dhcpTiming2 = 0x78; // 120 sec 54 constexpr uint16_t dhcpTiming3 = 0x40; // 60 sec 55 // When DHCP Option 12 is enabled the string "SendHostName=true" will be 56 // added into n/w configuration file and the parameter 57 // SendHostNameEnabled will set to true. 58 constexpr auto dhcpOpt12Enabled = "SendHostNameEnabled"; 59 60 enum class DCMIConfigParameters : uint8_t 61 { 62 ActivateDHCP = 1, 63 DiscoveryConfig, 64 DHCPTiming1, 65 DHCPTiming2, 66 DHCPTiming3, 67 }; 68 69 // Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec 70 static const std::map<uint8_t, std::string> entityIdToName{ 71 {0x40, "inlet"}, {0x37, "inlet"}, {0x41, "cpu"}, 72 {0x03, "cpu"}, {0x42, "baseboard"}, {0x07, "baseboard"}}; 73 74 nlohmann::json parseJSONConfig(const std::string& configFile) 75 { 76 std::ifstream jsonFile(configFile); 77 if (!jsonFile.is_open()) 78 { 79 lg2::error("Temperature readings JSON file not found"); 80 elog<InternalFailure>(); 81 } 82 83 auto data = nlohmann::json::parse(jsonFile, nullptr, false); 84 if (data.is_discarded()) 85 { 86 lg2::error("Temperature readings JSON parser failure"); 87 elog<InternalFailure>(); 88 } 89 90 return data; 91 } 92 93 bool isDCMIPowerMgmtSupported() 94 { 95 static bool parsed = false; 96 static bool supported = false; 97 if (!parsed) 98 { 99 auto data = parseJSONConfig(gDCMICapabilitiesConfig); 100 101 supported = (gDCMIPowerMgmtSupported == 102 data.value(gDCMIPowerMgmtCapability, 0)); 103 } 104 return supported; 105 } 106 107 std::optional<uint32_t> getPcap(ipmi::Context::ptr& ctx) 108 { 109 std::string service{}; 110 boost::system::error_code ec = ipmi::getService(ctx, pcapInterface, 111 pcapPath, service); 112 if (ec.value()) 113 { 114 return std::nullopt; 115 } 116 uint32_t pcap{}; 117 ec = ipmi::getDbusProperty(ctx, service, pcapPath, pcapInterface, 118 powerCapProp, pcap); 119 if (ec.value()) 120 { 121 lg2::error("Error in getPcap prop: {ERROR}", "ERROR", ec.message()); 122 elog<InternalFailure>(); 123 return std::nullopt; 124 } 125 return pcap; 126 } 127 128 std::optional<bool> getPcapEnabled(ipmi::Context::ptr& ctx) 129 { 130 std::string service{}; 131 boost::system::error_code ec = ipmi::getService(ctx, pcapInterface, 132 pcapPath, service); 133 if (ec.value()) 134 { 135 return std::nullopt; 136 } 137 bool pcapEnabled{}; 138 ec = ipmi::getDbusProperty(ctx, service, pcapPath, pcapInterface, 139 powerCapEnableProp, pcapEnabled); 140 if (ec.value()) 141 { 142 lg2::error("Error in getPcap prop"); 143 elog<InternalFailure>(); 144 return std::nullopt; 145 } 146 return pcapEnabled; 147 } 148 149 bool setPcap(ipmi::Context::ptr& ctx, const uint32_t powerCap) 150 { 151 std::string service{}; 152 boost::system::error_code ec = ipmi::getService(ctx, pcapInterface, 153 pcapPath, service); 154 if (ec.value()) 155 { 156 return false; 157 } 158 159 ec = ipmi::setDbusProperty(ctx, service, pcapPath, pcapInterface, 160 powerCapProp, powerCap); 161 if (ec.value()) 162 { 163 lg2::error("Error in setPcap property: {ERROR}", "ERROR", ec.message()); 164 elog<InternalFailure>(); 165 return false; 166 } 167 return true; 168 } 169 170 bool setPcapEnable(ipmi::Context::ptr& ctx, bool enabled) 171 { 172 std::string service{}; 173 boost::system::error_code ec = ipmi::getService(ctx, pcapInterface, 174 pcapPath, service); 175 if (ec.value()) 176 { 177 return false; 178 } 179 180 ec = ipmi::setDbusProperty(ctx, service, pcapPath, pcapInterface, 181 powerCapEnableProp, enabled); 182 if (ec.value()) 183 { 184 lg2::error("Error in setPcapEnabled property: {ERROR}", "ERROR", 185 ec.message()); 186 elog<InternalFailure>(); 187 return false; 188 } 189 return true; 190 } 191 192 std::optional<std::string> readAssetTag(ipmi::Context::ptr& ctx) 193 { 194 // Read the object tree with the inventory root to figure out the object 195 // that has implemented the Asset tag interface. 196 ipmi::DbusObjectInfo objectInfo; 197 boost::system::error_code ec = getDbusObject( 198 ctx, dcmi::assetTagIntf, ipmi::sensor::inventoryRoot, "", objectInfo); 199 if (ec.value()) 200 { 201 return std::nullopt; 202 } 203 204 std::string assetTag{}; 205 ec = ipmi::getDbusProperty(ctx, objectInfo.second, objectInfo.first, 206 dcmi::assetTagIntf, dcmi::assetTagProp, 207 assetTag); 208 if (ec.value()) 209 { 210 lg2::error("Error in reading asset tag: {ERROR}", "ERROR", 211 ec.message()); 212 elog<InternalFailure>(); 213 return std::nullopt; 214 } 215 216 return assetTag; 217 } 218 219 bool writeAssetTag(ipmi::Context::ptr& ctx, const std::string& assetTag) 220 { 221 // Read the object tree with the inventory root to figure out the object 222 // that has implemented the Asset tag interface. 223 ipmi::DbusObjectInfo objectInfo; 224 boost::system::error_code ec = getDbusObject( 225 ctx, dcmi::assetTagIntf, ipmi::sensor::inventoryRoot, "", objectInfo); 226 if (ec.value()) 227 { 228 return false; 229 } 230 231 ec = ipmi::setDbusProperty(ctx, objectInfo.second, objectInfo.first, 232 dcmi::assetTagIntf, dcmi::assetTagProp, 233 assetTag); 234 if (ec.value()) 235 { 236 lg2::error("Error in writing asset tag: {ERROR}", "ERROR", 237 ec.message()); 238 elog<InternalFailure>(); 239 return false; 240 } 241 return true; 242 } 243 244 std::optional<std::string> getHostName(ipmi::Context::ptr& ctx) 245 { 246 std::string service{}; 247 boost::system::error_code ec = ipmi::getService(ctx, networkConfigIntf, 248 networkConfigObj, service); 249 if (ec.value()) 250 { 251 return std::nullopt; 252 } 253 std::string hostname{}; 254 ec = ipmi::getDbusProperty(ctx, service, networkConfigObj, 255 networkConfigIntf, hostNameProp, hostname); 256 if (ec.value()) 257 { 258 lg2::error("Error fetching hostname"); 259 elog<InternalFailure>(); 260 return std::nullopt; 261 } 262 return hostname; 263 } 264 265 std::optional<EthernetInterface::DHCPConf> 266 getDHCPEnabled(ipmi::Context::ptr& ctx) 267 { 268 auto ethdevice = ipmi::getChannelName(ethernetDefaultChannelNum); 269 ipmi::DbusObjectInfo ethernetObj{}; 270 boost::system::error_code ec = ipmi::getDbusObject( 271 ctx, ethernetIntf, networkRoot, ethdevice, ethernetObj); 272 if (ec.value()) 273 { 274 return std::nullopt; 275 } 276 std::string service{}; 277 ec = ipmi::getService(ctx, ethernetIntf, ethernetObj.first, service); 278 if (ec.value()) 279 { 280 return std::nullopt; 281 } 282 std::string dhcpVal{}; 283 ec = ipmi::getDbusProperty(ctx, service, ethernetObj.first, ethernetIntf, 284 "DHCPEnabled", dhcpVal); 285 if (ec.value()) 286 { 287 return std::nullopt; 288 } 289 290 return EthernetInterface::convertDHCPConfFromString(dhcpVal); 291 } 292 293 std::optional<bool> getDHCPOption(ipmi::Context::ptr& ctx, 294 const std::string& prop) 295 { 296 ipmi::ObjectTree objectTree; 297 if (ipmi::getAllDbusObjects(ctx, networkRoot, dhcpIntf, objectTree)) 298 { 299 return std::nullopt; 300 } 301 302 for (const auto& [path, serviceMap] : objectTree) 303 { 304 for (const auto& [service, object] : serviceMap) 305 { 306 bool value{}; 307 if (ipmi::getDbusProperty(ctx, service, path, dhcpIntf, prop, 308 value)) 309 { 310 return std::nullopt; 311 } 312 313 if (value) 314 { 315 return true; 316 } 317 } 318 } 319 320 return false; 321 } 322 323 bool setDHCPOption(ipmi::Context::ptr& ctx, std::string prop, bool value) 324 { 325 ipmi::ObjectTree objectTree; 326 if (ipmi::getAllDbusObjects(ctx, networkRoot, dhcpIntf, objectTree)) 327 { 328 return false; 329 } 330 331 for (const auto& [path, serviceMap] : objectTree) 332 { 333 for (const auto& [service, object] : serviceMap) 334 { 335 if (ipmi::setDbusProperty(ctx, service, path, dhcpIntf, prop, 336 value)) 337 { 338 return false; 339 } 340 } 341 } 342 343 return true; 344 } 345 346 } // namespace dcmi 347 348 constexpr uint8_t exceptionPowerOff = 0x01; 349 ipmi::RspType<uint16_t, // reserved 350 uint8_t, // exception actions 351 uint16_t, // power limit requested in watts 352 uint32_t, // correction time in milliseconds 353 uint16_t, // reserved 354 uint16_t // statistics sampling period in seconds 355 > 356 getPowerLimit(ipmi::Context::ptr ctx, uint16_t reserved) 357 { 358 if (!dcmi::isDCMIPowerMgmtSupported()) 359 { 360 return ipmi::responseInvalidCommand(); 361 } 362 if (reserved) 363 { 364 return ipmi::responseInvalidFieldRequest(); 365 } 366 367 std::optional<uint16_t> pcapValue = dcmi::getPcap(ctx); 368 std::optional<bool> pcapEnable = dcmi::getPcapEnabled(ctx); 369 if (!pcapValue || !pcapEnable) 370 { 371 return ipmi::responseUnspecifiedError(); 372 } 373 374 constexpr uint16_t reserved1{}; 375 constexpr uint16_t reserved2{}; 376 /* 377 * Exception action if power limit is exceeded and cannot be controlled 378 * with the correction time limit is hardcoded to Hard Power Off system 379 * and log event to SEL. 380 */ 381 constexpr uint8_t exception = exceptionPowerOff; 382 /* 383 * Correction time limit and Statistics sampling period is currently not 384 * populated. 385 */ 386 constexpr uint32_t correctionTime{}; 387 constexpr uint16_t statsPeriod{}; 388 if (*pcapEnable == false) 389 { 390 constexpr ipmi::Cc responseNoPowerLimitSet = 0x80; 391 return ipmi::response(responseNoPowerLimitSet, reserved1, exception, 392 *pcapValue, correctionTime, reserved2, 393 statsPeriod); 394 } 395 return ipmi::responseSuccess(reserved1, exception, *pcapValue, 396 correctionTime, reserved2, statsPeriod); 397 } 398 399 ipmi::RspType<> setPowerLimit(ipmi::Context::ptr& ctx, uint16_t reserved1, 400 uint8_t reserved2, uint8_t exceptionAction, 401 uint16_t powerLimit, uint32_t correctionTime, 402 uint16_t reserved3, uint16_t statsPeriod) 403 { 404 if (!dcmi::isDCMIPowerMgmtSupported()) 405 { 406 lg2::error("DCMI Power management is unsupported!"); 407 return ipmi::responseInvalidCommand(); 408 } 409 410 // Only process the power limit requested in watts. Return errors 411 // for other fields that are set 412 if (reserved1 || reserved2 || reserved3 || correctionTime || statsPeriod || 413 exceptionAction != exceptionPowerOff) 414 { 415 return ipmi::responseInvalidFieldRequest(); 416 } 417 418 if (!dcmi::setPcap(ctx, powerLimit)) 419 { 420 return ipmi::responseUnspecifiedError(); 421 } 422 423 lg2::info("Set Power Cap: {POWERCAP}", "POWERCAP", powerLimit); 424 425 return ipmi::responseSuccess(); 426 } 427 428 ipmi::RspType<> applyPowerLimit(ipmi::Context::ptr& ctx, bool enabled, 429 uint7_t reserved1, uint16_t reserved2) 430 { 431 if (!dcmi::isDCMIPowerMgmtSupported()) 432 { 433 lg2::error("DCMI Power management is unsupported!"); 434 return ipmi::responseInvalidCommand(); 435 } 436 if (reserved1 || reserved2) 437 { 438 return ipmi::responseInvalidFieldRequest(); 439 } 440 441 if (!dcmi::setPcapEnable(ctx, enabled)) 442 { 443 return ipmi::responseUnspecifiedError(); 444 } 445 446 lg2::info("Set Power Cap Enable: {POWERCAPENABLE}", "POWERCAPENABLE", 447 enabled); 448 449 return ipmi::responseSuccess(); 450 } 451 452 ipmi::RspType<uint8_t, // total tag length 453 std::vector<char> // tag data 454 > 455 getAssetTag(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count) 456 { 457 // Verify offset to read and number of bytes to read are not exceeding 458 // the range. 459 if ((offset > dcmi::assetTagMaxOffset) || (count > dcmi::maxBytes) || 460 ((offset + count) > dcmi::assetTagMaxSize)) 461 { 462 return ipmi::responseParmOutOfRange(); 463 } 464 465 std::optional<std::string> assetTagResp = dcmi::readAssetTag(ctx); 466 if (!assetTagResp) 467 { 468 return ipmi::responseUnspecifiedError(); 469 } 470 471 std::string& assetTag = assetTagResp.value(); 472 // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to 473 // suit Get Asset Tag command. 474 if (assetTag.size() > dcmi::assetTagMaxSize) 475 { 476 assetTag.resize(dcmi::assetTagMaxSize); 477 } 478 479 if (offset >= assetTag.size()) 480 { 481 return ipmi::responseParmOutOfRange(); 482 } 483 484 // silently truncate reads beyond the end of assetTag 485 if ((offset + count) >= assetTag.size()) 486 { 487 count = assetTag.size() - offset; 488 } 489 490 auto totalTagSize = static_cast<uint8_t>(assetTag.size()); 491 std::vector<char> data{assetTag.begin() + offset, 492 assetTag.begin() + offset + count}; 493 494 return ipmi::responseSuccess(totalTagSize, data); 495 } 496 497 ipmi::RspType<uint8_t // new asset tag length 498 > 499 setAssetTag(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count, 500 const std::vector<char>& data) 501 { 502 // Verify offset to read and number of bytes to read are not exceeding 503 // the range. 504 if ((offset > dcmi::assetTagMaxOffset) || (count > dcmi::maxBytes) || 505 ((offset + count) > dcmi::assetTagMaxSize)) 506 { 507 return ipmi::responseParmOutOfRange(); 508 } 509 if (data.size() != count) 510 { 511 return ipmi::responseReqDataLenInvalid(); 512 } 513 514 std::optional<std::string> assetTagResp = dcmi::readAssetTag(ctx); 515 if (!assetTagResp) 516 { 517 return ipmi::responseUnspecifiedError(); 518 } 519 520 std::string& assetTag = assetTagResp.value(); 521 522 if (offset > assetTag.size()) 523 { 524 return ipmi::responseParmOutOfRange(); 525 } 526 527 // operation is to truncate at offset and append new data 528 assetTag.resize(offset); 529 assetTag.append(data.begin(), data.end()); 530 531 if (!dcmi::writeAssetTag(ctx, assetTag)) 532 { 533 return ipmi::responseUnspecifiedError(); 534 } 535 536 auto totalTagSize = static_cast<uint8_t>(assetTag.size()); 537 return ipmi::responseSuccess(totalTagSize); 538 } 539 540 ipmi::RspType<uint8_t, // length 541 std::vector<char> // data 542 > 543 getMgmntCtrlIdStr(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count) 544 { 545 if (count > dcmi::maxBytes || offset + count > dcmi::maxCtrlIdStrLen) 546 { 547 return ipmi::responseParmOutOfRange(); 548 } 549 550 std::optional<std::string> hostnameResp = dcmi::getHostName(ctx); 551 if (!hostnameResp) 552 { 553 return ipmi::responseUnspecifiedError(); 554 } 555 556 std::string& hostname = hostnameResp.value(); 557 // If the id string is longer than 63 bytes, restrict it to 63 bytes to 558 // suit set management ctrl str command. 559 if (hostname.size() > dcmi::maxCtrlIdStrLen) 560 { 561 hostname.resize(dcmi::maxCtrlIdStrLen); 562 } 563 564 if (offset >= hostname.size()) 565 { 566 return ipmi::responseParmOutOfRange(); 567 } 568 569 // silently truncate reads beyond the end of hostname 570 if ((offset + count) >= hostname.size()) 571 { 572 count = hostname.size() - offset; 573 } 574 575 auto nameSize = static_cast<uint8_t>(hostname.size()); 576 std::vector<char> data{hostname.begin() + offset, 577 hostname.begin() + offset + count}; 578 579 return ipmi::responseSuccess(nameSize, data); 580 } 581 582 ipmi::RspType<uint8_t> setMgmntCtrlIdStr(ipmi::Context::ptr& ctx, 583 uint8_t offset, uint8_t count, 584 std::vector<char> data) 585 { 586 if ((offset > dcmi::maxCtrlIdStrLen) || (count > dcmi::maxBytes) || 587 ((offset + count) > dcmi::maxCtrlIdStrLen)) 588 { 589 return ipmi::responseParmOutOfRange(); 590 } 591 if (data.size() != count) 592 { 593 return ipmi::responseReqDataLenInvalid(); 594 } 595 bool terminalWrite{data.back() == '\0'}; 596 if (terminalWrite) 597 { 598 // remove the null termination from the data (no need with std::string) 599 data.resize(count - 1); 600 } 601 602 static std::string hostname{}; 603 // read in the current value if not starting at offset 0 604 if (hostname.size() == 0 && offset != 0) 605 { 606 /* read old ctrlIdStr */ 607 std::optional<std::string> hostnameResp = dcmi::getHostName(ctx); 608 if (!hostnameResp) 609 { 610 return ipmi::responseUnspecifiedError(); 611 } 612 hostname = hostnameResp.value(); 613 hostname.resize(offset); 614 } 615 616 // operation is to truncate at offset and append new data 617 hostname.append(data.begin(), data.end()); 618 619 // do the update if this is the last write 620 if (terminalWrite) 621 { 622 boost::system::error_code ec = ipmi::setDbusProperty( 623 ctx, dcmi::networkServiceName, dcmi::networkConfigObj, 624 dcmi::networkConfigIntf, dcmi::hostNameProp, hostname); 625 hostname.clear(); 626 if (ec.value()) 627 { 628 return ipmi::responseUnspecifiedError(); 629 } 630 } 631 632 auto totalIdSize = static_cast<uint8_t>(offset + count); 633 return ipmi::responseSuccess(totalIdSize); 634 } 635 636 ipmi::RspType<ipmi::message::Payload> getDCMICapabilities(uint8_t parameter) 637 { 638 std::ifstream dcmiCapFile(dcmi::gDCMICapabilitiesConfig); 639 if (!dcmiCapFile.is_open()) 640 { 641 lg2::error("DCMI Capabilities file not found"); 642 return ipmi::responseUnspecifiedError(); 643 } 644 645 auto data = nlohmann::json::parse(dcmiCapFile, nullptr, false); 646 if (data.is_discarded()) 647 { 648 lg2::error("DCMI Capabilities JSON parser failure"); 649 return ipmi::responseUnspecifiedError(); 650 } 651 652 constexpr bool reserved1{}; 653 constexpr uint5_t reserved5{}; 654 constexpr uint7_t reserved7{}; 655 constexpr uint8_t reserved8{}; 656 constexpr uint16_t reserved16{}; 657 658 ipmi::message::Payload payload; 659 payload.pack(dcmi::specMajorVersion, dcmi::specMinorVersion, 660 dcmi::parameterRevision); 661 662 enum class DCMICapParameters : uint8_t 663 { 664 SupportedDcmiCaps = 0x01, // Supported DCMI Capabilities 665 MandatoryPlatAttributes = 0x02, // Mandatory Platform Attributes 666 OptionalPlatAttributes = 0x03, // Optional Platform Attributes 667 ManageabilityAccessAttributes = 0x04, // Manageability Access Attributes 668 }; 669 670 switch (static_cast<DCMICapParameters>(parameter)) 671 { 672 case DCMICapParameters::SupportedDcmiCaps: 673 { 674 bool powerManagement = data.value("PowerManagement", 0); 675 bool oobSecondaryLan = data.value("OOBSecondaryLan", 0); 676 bool serialTMode = data.value("SerialTMODE", 0); 677 bool inBandSystemInterfaceChannel = 678 data.value("InBandSystemInterfaceChannel", 0); 679 payload.pack(reserved8, powerManagement, reserved7, 680 inBandSystemInterfaceChannel, serialTMode, 681 oobSecondaryLan, reserved5); 682 break; 683 } 684 // Mandatory Platform Attributes 685 case DCMICapParameters::MandatoryPlatAttributes: 686 { 687 bool selAutoRollOver = data.value("SELAutoRollOver", 0); 688 bool flushEntireSELUponRollOver = 689 data.value("FlushEntireSELUponRollOver", 0); 690 bool recordLevelSELFlushUponRollOver = 691 data.value("RecordLevelSELFlushUponRollOver", 0); 692 uint12_t numberOfSELEntries = data.value("NumberOfSELEntries", 693 0xcac); 694 uint8_t tempMonitoringSamplingFreq = 695 data.value("TempMonitoringSamplingFreq", 0); 696 payload.pack(numberOfSELEntries, reserved1, 697 recordLevelSELFlushUponRollOver, 698 flushEntireSELUponRollOver, selAutoRollOver, 699 reserved16, tempMonitoringSamplingFreq); 700 break; 701 } 702 // Optional Platform Attributes 703 case DCMICapParameters::OptionalPlatAttributes: 704 { 705 uint7_t powerMgmtDeviceTargetAddress = 706 data.value("PowerMgmtDeviceSlaveAddress", 0); 707 uint4_t bmcChannelNumber = data.value("BMCChannelNumber", 0); 708 uint4_t deviceRivision = data.value("DeviceRivision", 0); 709 payload.pack(powerMgmtDeviceTargetAddress, reserved1, 710 deviceRivision, bmcChannelNumber); 711 break; 712 } 713 // Manageability Access Attributes 714 case DCMICapParameters::ManageabilityAccessAttributes: 715 { 716 uint8_t mandatoryPrimaryLanOOBSupport = 717 data.value("MandatoryPrimaryLanOOBSupport", 0xff); 718 uint8_t optionalSecondaryLanOOBSupport = 719 data.value("OptionalSecondaryLanOOBSupport", 0xff); 720 uint8_t optionalSerialOOBMTMODECapability = 721 data.value("OptionalSerialOOBMTMODECapability", 0xff); 722 payload.pack(mandatoryPrimaryLanOOBSupport, 723 optionalSecondaryLanOOBSupport, 724 optionalSerialOOBMTMODECapability); 725 break; 726 } 727 default: 728 { 729 lg2::error("Invalid input parameter"); 730 return ipmi::responseInvalidFieldRequest(); 731 } 732 } 733 734 return ipmi::responseSuccess(payload); 735 } 736 737 namespace dcmi 738 { 739 namespace temp_readings 740 { 741 742 std::tuple<bool, bool, uint8_t> readTemp(ipmi::Context::ptr& ctx, 743 const std::string& dbusService, 744 const std::string& dbusPath) 745 { 746 // Read the temperature value from d-bus object. Need some conversion. 747 // As per the interface xyz.openbmc_project.Sensor.Value, the 748 // temperature is an double and in degrees C. It needs to be scaled by 749 // using the formula Value * 10^Scale. The ipmi spec has the temperature 750 // as a uint8_t, with a separate single bit for the sign. 751 752 ipmi::PropertyMap result{}; 753 boost::system::error_code ec = ipmi::getAllDbusProperties( 754 ctx, dbusService, dbusPath, "xyz.openbmc_project.Sensor.Value", result); 755 if (ec.value()) 756 { 757 return std::make_tuple(false, false, 0); 758 } 759 auto temperature = std::visit(ipmi::VariantToDoubleVisitor(), 760 result.at("Value")); 761 double absTemp = std::abs(temperature); 762 763 auto findFactor = result.find("Scale"); 764 double factor = 0.0; 765 if (findFactor != result.end()) 766 { 767 factor = std::visit(ipmi::VariantToDoubleVisitor(), findFactor->second); 768 } 769 double scale = std::pow(10, factor); 770 771 auto tempDegrees = absTemp * scale; 772 // Max absolute temp as per ipmi spec is 127. 773 constexpr auto maxTemp = 127; 774 if (tempDegrees > maxTemp) 775 { 776 tempDegrees = maxTemp; 777 } 778 779 return std::make_tuple(true, (temperature < 0), 780 static_cast<uint8_t>(tempDegrees)); 781 } 782 783 std::tuple<std::vector<std::tuple<uint7_t, bool, uint8_t>>, uint8_t> 784 read(ipmi::Context::ptr& ctx, const std::string& type, uint8_t instance, 785 size_t count) 786 { 787 std::vector<std::tuple<uint7_t, bool, uint8_t>> response{}; 788 789 auto data = parseJSONConfig(gDCMISensorsConfig); 790 static const std::vector<nlohmann::json> empty{}; 791 std::vector<nlohmann::json> readings = data.value(type, empty); 792 for (const auto& j : readings) 793 { 794 // Max of 8 response data sets 795 if (response.size() == count) 796 { 797 break; 798 } 799 800 uint8_t instanceNum = j.value("instance", 0); 801 // Not in the instance range we're interested in 802 if (instanceNum < instance) 803 { 804 continue; 805 } 806 807 std::string path = j.value("dbus", ""); 808 std::string service{}; 809 boost::system::error_code ec = ipmi::getService( 810 ctx, "xyz.openbmc_project.Sensor.Value", path, service); 811 if (ec.value()) 812 { 813 // not found on dbus 814 continue; 815 } 816 817 const auto& [ok, sign, temp] = readTemp(ctx, service, path); 818 if (ok) 819 { 820 response.emplace_back(uint7_t{temp}, sign, instanceNum); 821 } 822 } 823 824 auto totalInstances = 825 static_cast<uint8_t>(std::min(readings.size(), maxInstances)); 826 return std::make_tuple(response, totalInstances); 827 } 828 829 } // namespace temp_readings 830 } // namespace dcmi 831 832 ipmi::RspType<uint8_t, // total instances for entity id 833 uint8_t, // number of instances in this reply 834 std::vector< // zero or more of the following two bytes 835 std::tuple<uint7_t, // temperature value 836 bool, // sign bit 837 uint8_t // entity instance 838 >>> 839 getTempReadings(ipmi::Context::ptr& ctx, uint8_t sensorType, 840 uint8_t entityId, uint8_t entityInstance, 841 uint8_t instanceStart) 842 { 843 auto it = dcmi::entityIdToName.find(entityId); 844 if (it == dcmi::entityIdToName.end()) 845 { 846 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId); 847 return ipmi::responseInvalidFieldRequest(); 848 } 849 850 if (sensorType != dcmi::temperatureSensorType) 851 { 852 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE", 853 sensorType); 854 return ipmi::responseInvalidFieldRequest(); 855 } 856 857 uint8_t requestedRecords = (entityInstance == 0) ? dcmi::maxRecords : 1; 858 859 // Read requested instances 860 const auto& [temps, totalInstances] = dcmi::temp_readings::read( 861 ctx, it->second, instanceStart, requestedRecords); 862 863 auto numInstances = static_cast<uint8_t>(temps.size()); 864 865 return ipmi::responseSuccess(totalInstances, numInstances, temps); 866 } 867 868 ipmi::RspType<> setDCMIConfParams(ipmi::Context::ptr& ctx, uint8_t parameter, 869 uint8_t setSelector, 870 ipmi::message::Payload& payload) 871 { 872 if (setSelector) 873 { 874 return ipmi::responseInvalidFieldRequest(); 875 } 876 // Take action based on the Parameter Selector 877 switch (static_cast<dcmi::DCMIConfigParameters>(parameter)) 878 { 879 case dcmi::DCMIConfigParameters::ActivateDHCP: 880 { 881 uint7_t reserved{}; 882 bool activate{}; 883 if (payload.unpack(activate, reserved) || !payload.fullyUnpacked()) 884 { 885 return ipmi::responseReqDataLenInvalid(); 886 } 887 if (reserved) 888 { 889 return ipmi::responseInvalidFieldRequest(); 890 } 891 std::optional<EthernetInterface::DHCPConf> dhcpEnabled = 892 dcmi::getDHCPEnabled(ctx); 893 if (!dhcpEnabled) 894 { 895 return ipmi::responseUnspecifiedError(); 896 } 897 if (activate && 898 (dhcpEnabled.value() != EthernetInterface::DHCPConf::none)) 899 { 900 // When these conditions are met we have to trigger DHCP 901 // protocol restart using the latest parameter settings, 902 // but as per n/w manager design, each time when we 903 // update n/w parameters, n/w service is restarted. So 904 // we no need to take any action in this case. 905 } 906 break; 907 } 908 case dcmi::DCMIConfigParameters::DiscoveryConfig: 909 { 910 bool option12{}; 911 uint6_t reserved1{}; 912 bool randBackOff{}; 913 if (payload.unpack(option12, reserved1, randBackOff) || 914 !payload.fullyUnpacked()) 915 { 916 return ipmi::responseReqDataLenInvalid(); 917 } 918 // Systemd-networkd doesn't support Random Back off 919 if (reserved1 || randBackOff) 920 { 921 return ipmi::responseInvalidFieldRequest(); 922 } 923 dcmi::setDHCPOption(ctx, dcmi::dhcpOpt12Enabled, option12); 924 break; 925 } 926 // Systemd-networkd doesn't allow to configure DHCP timigs 927 case dcmi::DCMIConfigParameters::DHCPTiming1: 928 case dcmi::DCMIConfigParameters::DHCPTiming2: 929 case dcmi::DCMIConfigParameters::DHCPTiming3: 930 default: 931 return ipmi::responseInvalidFieldRequest(); 932 } 933 return ipmi::responseSuccess(); 934 } 935 936 ipmi::RspType<ipmi::message::Payload> getDCMIConfParams(ipmi::Context::ptr& ctx, 937 uint8_t parameter, 938 uint8_t setSelector) 939 { 940 if (setSelector) 941 { 942 return ipmi::responseInvalidFieldRequest(); 943 } 944 ipmi::message::Payload payload; 945 payload.pack(dcmi::specMajorVersion, dcmi::specMinorVersion, 946 dcmi::configParameterRevision); 947 948 // Take action based on the Parameter Selector 949 switch (static_cast<dcmi::DCMIConfigParameters>(parameter)) 950 { 951 case dcmi::DCMIConfigParameters::ActivateDHCP: 952 payload.pack(dcmi::activateDhcpReply); 953 break; 954 case dcmi::DCMIConfigParameters::DiscoveryConfig: 955 { 956 uint8_t discovery{}; 957 std::optional<bool> enabled = 958 dcmi::getDHCPOption(ctx, dcmi::dhcpOpt12Enabled); 959 if (!enabled.has_value()) 960 { 961 return ipmi::responseUnspecifiedError(); 962 } 963 if (enabled.value()) 964 { 965 discovery = dcmi::option12Mask; 966 } 967 payload.pack(discovery); 968 break; 969 } 970 // Get below values from Systemd-networkd source code 971 case dcmi::DCMIConfigParameters::DHCPTiming1: 972 payload.pack(dcmi::dhcpTiming1); 973 break; 974 case dcmi::DCMIConfigParameters::DHCPTiming2: 975 payload.pack(dcmi::dhcpTiming2); 976 break; 977 case dcmi::DCMIConfigParameters::DHCPTiming3: 978 payload.pack(dcmi::dhcpTiming3); 979 break; 980 default: 981 return ipmi::responseInvalidFieldRequest(); 982 } 983 984 return ipmi::responseSuccess(payload); 985 } 986 987 static std::optional<uint16_t> readPower(ipmi::Context::ptr& ctx) 988 { 989 std::ifstream sensorFile(POWER_READING_SENSOR); 990 std::string objectPath; 991 if (!sensorFile.is_open()) 992 { 993 lg2::error( 994 "Power reading configuration file not found: {POWER_SENSOR_FILE}", 995 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR}); 996 return std::nullopt; 997 } 998 999 auto data = nlohmann::json::parse(sensorFile, nullptr, false); 1000 if (data.is_discarded()) 1001 { 1002 lg2::error("Error in parsing configuration file: {POWER_SENSOR_FILE}", 1003 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR}); 1004 return std::nullopt; 1005 } 1006 1007 objectPath = data.value("path", ""); 1008 if (objectPath.empty()) 1009 { 1010 lg2::error( 1011 "Power sensor D-Bus object path is empty: {POWER_SENSOR_FILE}", 1012 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR}); 1013 return std::nullopt; 1014 } 1015 1016 // Return default value if failed to read from D-Bus object 1017 std::string service{}; 1018 boost::system::error_code ec = ipmi::getService(ctx, dcmi::sensorValueIntf, 1019 objectPath, service); 1020 if (ec.value()) 1021 { 1022 lg2::error("Failed to fetch service for D-Bus object, " 1023 "object path: {OBJECT_PATH}, interface: {INTERFACE}", 1024 "OBJECT_PATH", objectPath, "INTERFACE", 1025 dcmi::sensorValueIntf); 1026 return std::nullopt; 1027 } 1028 1029 // Read the sensor value and scale properties 1030 double value{}; 1031 ec = ipmi::getDbusProperty(ctx, service, objectPath, dcmi::sensorValueIntf, 1032 dcmi::sensorValueProp, value); 1033 if (ec.value()) 1034 { 1035 lg2::error("Failed to read power value from D-Bus object, " 1036 "object path: {OBJECT_PATH}, interface: {INTERFACE}", 1037 "OBJECT_PATH", objectPath, "INTERFACE", 1038 dcmi::sensorValueIntf); 1039 return std::nullopt; 1040 } 1041 auto power = static_cast<uint16_t>(value); 1042 return power; 1043 } 1044 1045 ipmi::RspType<uint16_t, // current power 1046 uint16_t, // minimum power 1047 uint16_t, // maximum power 1048 uint16_t, // average power 1049 uint32_t, // timestamp 1050 uint32_t, // sample period ms 1051 uint6_t, // reserved 1052 bool, // power measurement active 1053 bool // reserved 1054 > 1055 getPowerReading(ipmi::Context::ptr& ctx, uint8_t mode, uint8_t attributes, 1056 uint8_t reserved) 1057 { 1058 if (!dcmi::isDCMIPowerMgmtSupported()) 1059 { 1060 lg2::error("DCMI Power management is unsupported!"); 1061 return ipmi::responseInvalidCommand(); 1062 } 1063 if (reserved) 1064 { 1065 return ipmi::responseInvalidFieldRequest(); 1066 } 1067 1068 enum class PowerMode : uint8_t 1069 { 1070 SystemPowerStatistics = 1, 1071 EnhancedSystemPowerStatistics = 2, 1072 }; 1073 1074 if (static_cast<PowerMode>(mode) != PowerMode::SystemPowerStatistics) 1075 { 1076 return ipmi::responseInvalidFieldRequest(); 1077 } 1078 if (attributes) 1079 { 1080 return ipmi::responseInvalidFieldRequest(); 1081 } 1082 1083 std::optional<uint16_t> powerResp = readPower(ctx); 1084 if (!powerResp) 1085 { 1086 return ipmi::responseUnspecifiedError(); 1087 } 1088 auto& power = powerResp.value(); 1089 1090 // TODO: openbmc/openbmc#2819 1091 // Minimum, Maximum, Average power, TimeFrame, TimeStamp, 1092 // PowerReadingState readings need to be populated 1093 // after Telemetry changes. 1094 constexpr uint32_t samplePeriod = 1; 1095 constexpr uint6_t reserved1 = 0; 1096 constexpr bool measurementActive = true; 1097 constexpr bool reserved2 = false; 1098 auto timestamp = static_cast<uint32_t>(time(nullptr)); 1099 return ipmi::responseSuccess(power, power, power, power, timestamp, 1100 samplePeriod, reserved1, measurementActive, 1101 reserved2); 1102 } 1103 1104 namespace dcmi 1105 { 1106 namespace sensor_info 1107 { 1108 1109 std::tuple<std::vector<uint16_t>, uint8_t> read(const std::string& type, 1110 uint8_t instance, 1111 const nlohmann::json& config, 1112 uint8_t count) 1113 { 1114 std::vector<uint16_t> responses{}; 1115 1116 static const std::vector<nlohmann::json> empty{}; 1117 std::vector<nlohmann::json> readings = config.value(type, empty); 1118 uint8_t totalInstances = std::min(readings.size(), maxInstances); 1119 for (const auto& reading : readings) 1120 { 1121 // limit to requested count 1122 if (responses.size() == count) 1123 { 1124 break; 1125 } 1126 1127 uint8_t instanceNum = reading.value("instance", 0); 1128 // Not in the instance range we're interested in 1129 if (instanceNum < instance) 1130 { 1131 continue; 1132 } 1133 1134 uint16_t recordId = reading.value("record_id", 0); 1135 responses.emplace_back(recordId); 1136 } 1137 1138 return std::make_tuple(responses, totalInstances); 1139 } 1140 1141 } // namespace sensor_info 1142 } // namespace dcmi 1143 1144 ipmi::RspType<uint8_t, // total available instances 1145 uint8_t, // number of records in this response 1146 std::vector<uint16_t> // records 1147 > 1148 getSensorInfo(uint8_t sensorType, uint8_t entityId, uint8_t entityInstance, 1149 uint8_t instanceStart) 1150 { 1151 auto it = dcmi::entityIdToName.find(entityId); 1152 if (it == dcmi::entityIdToName.end()) 1153 { 1154 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId); 1155 return ipmi::responseInvalidFieldRequest(); 1156 } 1157 1158 if (sensorType != dcmi::temperatureSensorType) 1159 { 1160 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE", 1161 sensorType); 1162 return ipmi::responseInvalidFieldRequest(); 1163 } 1164 1165 nlohmann::json config = dcmi::parseJSONConfig(dcmi::gDCMISensorsConfig); 1166 1167 uint8_t requestedRecords = (entityInstance == 0) ? dcmi::maxRecords : 1; 1168 // Read requested instances 1169 const auto& [sensors, totalInstances] = dcmi::sensor_info::read( 1170 it->second, instanceStart, config, requestedRecords); 1171 uint8_t numRecords = sensors.size(); 1172 1173 return ipmi::responseSuccess(totalInstances, numRecords, sensors); 1174 } 1175 1176 void register_netfn_dcmi_functions() 1177 { 1178 // <Get Power Limit> 1179 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1180 ipmi::dcmi::cmdGetPowerLimit, ipmi::Privilege::User, 1181 getPowerLimit); 1182 1183 // <Set Power Limit> 1184 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1185 ipmi::dcmi::cmdSetPowerLimit, 1186 ipmi::Privilege::Operator, setPowerLimit); 1187 1188 // <Activate/Deactivate Power Limit> 1189 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1190 ipmi::dcmi::cmdActDeactivatePwrLimit, 1191 ipmi::Privilege::Operator, applyPowerLimit); 1192 1193 // <Get Asset Tag> 1194 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1195 ipmi::dcmi::cmdGetAssetTag, ipmi::Privilege::User, 1196 getAssetTag); 1197 1198 // <Set Asset Tag> 1199 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1200 ipmi::dcmi::cmdSetAssetTag, ipmi::Privilege::Operator, 1201 setAssetTag); 1202 1203 // <Get Management Controller Identifier String> 1204 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1205 ipmi::dcmi::cmdGetMgmtCntlrIdString, 1206 ipmi::Privilege::User, getMgmntCtrlIdStr); 1207 1208 // <Set Management Controller Identifier String> 1209 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1210 ipmi::dcmi::cmdSetMgmtCntlrIdString, 1211 ipmi::Privilege::Admin, setMgmntCtrlIdStr); 1212 1213 // <Get DCMI capabilities> 1214 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1215 ipmi::dcmi::cmdGetDcmiCapabilitiesInfo, 1216 ipmi::Privilege::User, getDCMICapabilities); 1217 1218 // <Get Power Reading> 1219 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1220 ipmi::dcmi::cmdGetPowerReading, ipmi::Privilege::User, 1221 getPowerReading); 1222 1223 // The Get sensor should get the senor details dynamically when 1224 // FEATURE_DYNAMIC_SENSORS is enabled. 1225 #ifndef FEATURE_DYNAMIC_SENSORS 1226 // <Get Sensor Info> 1227 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1228 ipmi::dcmi::cmdGetDcmiSensorInfo, 1229 ipmi::Privilege::Operator, getSensorInfo); 1230 1231 // <Get Temperature Readings> 1232 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1233 ipmi::dcmi::cmdGetTemperatureReadings, 1234 ipmi::Privilege::User, getTempReadings); 1235 #endif 1236 // <Get DCMI Configuration Parameters> 1237 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1238 ipmi::dcmi::cmdGetDcmiConfigParameters, 1239 ipmi::Privilege::User, getDCMIConfParams); 1240 1241 // <Set DCMI Configuration Parameters> 1242 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 1243 ipmi::dcmi::cmdSetDcmiConfigParameters, 1244 ipmi::Privilege::Admin, setDCMIConfParams); 1245 1246 return; 1247 } 1248