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