1 #include "config.h" 2 3 #include "dcmihandler.hpp" 4 5 #include "user_channel/channel_layer.hpp" 6 7 #include <bitset> 8 #include <cmath> 9 #include <fstream> 10 #include <ipmid/api.hpp> 11 #include <ipmid/utils.hpp> 12 #include <nlohmann/json.hpp> 13 #include <phosphor-logging/elog-errors.hpp> 14 #include <phosphor-logging/log.hpp> 15 #include <sdbusplus/bus.hpp> 16 #include <variant> 17 #include <xyz/openbmc_project/Common/error.hpp> 18 19 using namespace phosphor::logging; 20 using InternalFailure = 21 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 22 23 void register_netfn_dcmi_functions() __attribute__((constructor)); 24 25 constexpr auto PCAP_PATH = "/xyz/openbmc_project/control/host0/power_cap"; 26 constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap"; 27 28 constexpr auto POWER_CAP_PROP = "PowerCap"; 29 constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable"; 30 31 constexpr auto DCMI_PARAMETER_REVISION = 2; 32 constexpr auto DCMI_SPEC_MAJOR_VERSION = 1; 33 constexpr auto DCMI_SPEC_MINOR_VERSION = 5; 34 constexpr auto DCMI_CONFIG_PARAMETER_REVISION = 1; 35 constexpr auto DCMI_RAND_BACK_OFF_MASK = 0x80; 36 constexpr auto DCMI_OPTION_60_43_MASK = 0x02; 37 constexpr auto DCMI_OPTION_12_MASK = 0x01; 38 constexpr auto DCMI_ACTIVATE_DHCP_MASK = 0x01; 39 constexpr auto DCMI_ACTIVATE_DHCP_REPLY = 0x00; 40 constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE = 0x04; 41 constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE = 0x03; 42 constexpr auto DHCP_TIMING1 = 0x04; // 4 sec 43 constexpr auto DHCP_TIMING2_UPPER = 0x00; // 2 min 44 constexpr auto DHCP_TIMING2_LOWER = 0x78; 45 constexpr auto DHCP_TIMING3_UPPER = 0x00; // 64 sec 46 constexpr auto DHCP_TIMING3_LOWER = 0x40; 47 // When DHCP Option 12 is enabled the string "SendHostName=true" will be 48 // added into n/w configuration file and the parameter 49 // SendHostNameEnabled will set to true. 50 constexpr auto DHCP_OPT12_ENABLED = "SendHostNameEnabled"; 51 52 constexpr auto SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value"; 53 constexpr auto SENSOR_VALUE_PROP = "Value"; 54 55 using namespace phosphor::logging; 56 57 namespace dcmi 58 { 59 60 // Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec 61 static const std::map<uint8_t, std::string> entityIdToName{ 62 {0x40, "inlet"}, {0x37, "inlet"}, {0x41, "cpu"}, 63 {0x03, "cpu"}, {0x42, "baseboard"}, {0x07, "baseboard"}}; 64 65 bool isDCMIPowerMgmtSupported() 66 { 67 auto data = parseJSONConfig(gDCMICapabilitiesConfig); 68 69 return (gDCMIPowerMgmtSupported == data.value(gDCMIPowerMgmtCapability, 0)); 70 } 71 72 uint32_t getPcap(sdbusplus::bus::bus& bus) 73 { 74 auto settingService = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH); 75 76 auto method = bus.new_method_call(settingService.c_str(), PCAP_PATH, 77 "org.freedesktop.DBus.Properties", "Get"); 78 79 method.append(PCAP_INTERFACE, POWER_CAP_PROP); 80 auto reply = bus.call(method); 81 82 if (reply.is_method_error()) 83 { 84 log<level::ERR>("Error in getPcap prop"); 85 elog<InternalFailure>(); 86 } 87 std::variant<uint32_t> pcap; 88 reply.read(pcap); 89 90 return std::get<uint32_t>(pcap); 91 } 92 93 bool getPcapEnabled(sdbusplus::bus::bus& bus) 94 { 95 auto settingService = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH); 96 97 auto method = bus.new_method_call(settingService.c_str(), PCAP_PATH, 98 "org.freedesktop.DBus.Properties", "Get"); 99 100 method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP); 101 auto reply = bus.call(method); 102 103 if (reply.is_method_error()) 104 { 105 log<level::ERR>("Error in getPcapEnabled prop"); 106 elog<InternalFailure>(); 107 } 108 std::variant<bool> pcapEnabled; 109 reply.read(pcapEnabled); 110 111 return std::get<bool>(pcapEnabled); 112 } 113 114 void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap) 115 { 116 auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH); 117 118 auto method = bus.new_method_call(service.c_str(), PCAP_PATH, 119 "org.freedesktop.DBus.Properties", "Set"); 120 121 method.append(PCAP_INTERFACE, POWER_CAP_PROP); 122 method.append(std::variant<uint32_t>(powerCap)); 123 124 auto reply = bus.call(method); 125 126 if (reply.is_method_error()) 127 { 128 log<level::ERR>("Error in setPcap property"); 129 elog<InternalFailure>(); 130 } 131 } 132 133 void setPcapEnable(sdbusplus::bus::bus& bus, bool enabled) 134 { 135 auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH); 136 137 auto method = bus.new_method_call(service.c_str(), PCAP_PATH, 138 "org.freedesktop.DBus.Properties", "Set"); 139 140 method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP); 141 method.append(std::variant<bool>(enabled)); 142 143 auto reply = bus.call(method); 144 145 if (reply.is_method_error()) 146 { 147 log<level::ERR>("Error in setPcapEnabled property"); 148 elog<InternalFailure>(); 149 } 150 } 151 152 void readAssetTagObjectTree(dcmi::assettag::ObjectTree& objectTree) 153 { 154 static constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper"; 155 static constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper"; 156 static constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper"; 157 static constexpr auto inventoryRoot = "/xyz/openbmc_project/inventory/"; 158 159 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 160 auto depth = 0; 161 162 auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath, 163 mapperIface, "GetSubTree"); 164 165 mapperCall.append(inventoryRoot); 166 mapperCall.append(depth); 167 mapperCall.append(std::vector<std::string>({dcmi::assetTagIntf})); 168 169 auto mapperReply = bus.call(mapperCall); 170 if (mapperReply.is_method_error()) 171 { 172 log<level::ERR>("Error in mapper call"); 173 elog<InternalFailure>(); 174 } 175 176 mapperReply.read(objectTree); 177 178 if (objectTree.empty()) 179 { 180 log<level::ERR>("AssetTag property is not populated"); 181 elog<InternalFailure>(); 182 } 183 } 184 185 std::string readAssetTag() 186 { 187 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 188 dcmi::assettag::ObjectTree objectTree; 189 190 // Read the object tree with the inventory root to figure out the object 191 // that has implemented the Asset tag interface. 192 readAssetTagObjectTree(objectTree); 193 194 auto method = bus.new_method_call( 195 (objectTree.begin()->second.begin()->first).c_str(), 196 (objectTree.begin()->first).c_str(), dcmi::propIntf, "Get"); 197 method.append(dcmi::assetTagIntf); 198 method.append(dcmi::assetTagProp); 199 200 auto reply = bus.call(method); 201 if (reply.is_method_error()) 202 { 203 log<level::ERR>("Error in reading asset tag"); 204 elog<InternalFailure>(); 205 } 206 207 std::variant<std::string> assetTag; 208 reply.read(assetTag); 209 210 return std::get<std::string>(assetTag); 211 } 212 213 void writeAssetTag(const std::string& assetTag) 214 { 215 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 216 dcmi::assettag::ObjectTree objectTree; 217 218 // Read the object tree with the inventory root to figure out the object 219 // that has implemented the Asset tag interface. 220 readAssetTagObjectTree(objectTree); 221 222 auto method = bus.new_method_call( 223 (objectTree.begin()->second.begin()->first).c_str(), 224 (objectTree.begin()->first).c_str(), dcmi::propIntf, "Set"); 225 method.append(dcmi::assetTagIntf); 226 method.append(dcmi::assetTagProp); 227 method.append(std::variant<std::string>(assetTag)); 228 229 auto reply = bus.call(method); 230 if (reply.is_method_error()) 231 { 232 log<level::ERR>("Error in writing asset tag"); 233 elog<InternalFailure>(); 234 } 235 } 236 237 std::string getHostName(void) 238 { 239 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 240 241 auto service = ipmi::getService(bus, networkConfigIntf, networkConfigObj); 242 auto value = ipmi::getDbusProperty(bus, service, networkConfigObj, 243 networkConfigIntf, hostNameProp); 244 245 return std::get<std::string>(value); 246 } 247 248 bool getDHCPEnabled() 249 { 250 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 251 252 auto ethdevice = ipmi::getChannelName(ethernetDefaultChannelNum); 253 auto ethernetObj = 254 ipmi::getDbusObject(bus, ethernetIntf, networkRoot, ethdevice); 255 auto service = ipmi::getService(bus, ethernetIntf, ethernetObj.first); 256 auto value = ipmi::getDbusProperty(bus, service, ethernetObj.first, 257 ethernetIntf, "DHCPEnabled"); 258 259 return std::get<bool>(value); 260 } 261 262 bool getDHCPOption(std::string prop) 263 { 264 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 265 266 auto service = ipmi::getService(bus, dhcpIntf, dhcpObj); 267 auto value = ipmi::getDbusProperty(bus, service, dhcpObj, dhcpIntf, prop); 268 269 return std::get<bool>(value); 270 } 271 272 void setDHCPOption(std::string prop, bool value) 273 { 274 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 275 276 auto service = ipmi::getService(bus, dhcpIntf, dhcpObj); 277 ipmi::setDbusProperty(bus, service, dhcpObj, dhcpIntf, prop, value); 278 } 279 280 Json parseJSONConfig(const std::string& configFile) 281 { 282 std::ifstream jsonFile(configFile); 283 if (!jsonFile.is_open()) 284 { 285 log<level::ERR>("Temperature readings JSON file not found"); 286 elog<InternalFailure>(); 287 } 288 289 auto data = Json::parse(jsonFile, nullptr, false); 290 if (data.is_discarded()) 291 { 292 log<level::ERR>("Temperature readings JSON parser failure"); 293 elog<InternalFailure>(); 294 } 295 296 return data; 297 } 298 299 } // namespace dcmi 300 301 ipmi_ret_t getPowerLimit(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, 302 ipmi_response_t response, ipmi_data_len_t data_len, 303 ipmi_context_t) 304 { 305 if (!dcmi::isDCMIPowerMgmtSupported()) 306 { 307 *data_len = 0; 308 log<level::ERR>("DCMI Power management is unsupported!"); 309 return IPMI_CC_INVALID; 310 } 311 312 std::vector<uint8_t> outPayload(sizeof(dcmi::GetPowerLimitResponse)); 313 auto responseData = 314 reinterpret_cast<dcmi::GetPowerLimitResponse*>(outPayload.data()); 315 316 sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()}; 317 uint32_t pcapValue = 0; 318 bool pcapEnable = false; 319 320 try 321 { 322 pcapValue = dcmi::getPcap(sdbus); 323 pcapEnable = dcmi::getPcapEnabled(sdbus); 324 } 325 catch (const InternalFailure& e) 326 { 327 *data_len = 0; 328 return IPMI_CC_UNSPECIFIED_ERROR; 329 } 330 331 /* 332 * Exception action if power limit is exceeded and cannot be controlled 333 * with the correction time limit is hardcoded to Hard Power Off system 334 * and log event to SEL. 335 */ 336 constexpr auto exception = 0x01; 337 responseData->exceptionAction = exception; 338 339 responseData->powerLimit = static_cast<uint16_t>(pcapValue); 340 341 /* 342 * Correction time limit and Statistics sampling period is currently not 343 * populated. 344 */ 345 346 *data_len = outPayload.size(); 347 memcpy(response, outPayload.data(), *data_len); 348 349 if (pcapEnable) 350 { 351 return IPMI_CC_OK; 352 } 353 else 354 { 355 return IPMI_DCMI_CC_NO_ACTIVE_POWER_LIMIT; 356 } 357 } 358 359 ipmi_ret_t setPowerLimit(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 360 ipmi_response_t, ipmi_data_len_t data_len, 361 ipmi_context_t) 362 { 363 if (!dcmi::isDCMIPowerMgmtSupported()) 364 { 365 *data_len = 0; 366 log<level::ERR>("DCMI Power management is unsupported!"); 367 return IPMI_CC_INVALID; 368 } 369 370 auto requestData = 371 reinterpret_cast<const dcmi::SetPowerLimitRequest*>(request); 372 373 sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()}; 374 375 // Only process the power limit requested in watts. 376 try 377 { 378 dcmi::setPcap(sdbus, requestData->powerLimit); 379 } 380 catch (const InternalFailure& e) 381 { 382 *data_len = 0; 383 return IPMI_CC_UNSPECIFIED_ERROR; 384 } 385 386 log<level::INFO>("Set Power Cap", 387 entry("POWERCAP=%u", requestData->powerLimit)); 388 389 *data_len = 0; 390 return IPMI_CC_OK; 391 } 392 393 ipmi_ret_t applyPowerLimit(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 394 ipmi_response_t, ipmi_data_len_t data_len, 395 ipmi_context_t) 396 { 397 if (!dcmi::isDCMIPowerMgmtSupported()) 398 { 399 *data_len = 0; 400 log<level::ERR>("DCMI Power management is unsupported!"); 401 return IPMI_CC_INVALID; 402 } 403 404 auto requestData = 405 reinterpret_cast<const dcmi::ApplyPowerLimitRequest*>(request); 406 407 sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()}; 408 409 try 410 { 411 dcmi::setPcapEnable(sdbus, 412 static_cast<bool>(requestData->powerLimitAction)); 413 } 414 catch (const InternalFailure& e) 415 { 416 *data_len = 0; 417 return IPMI_CC_UNSPECIFIED_ERROR; 418 } 419 420 log<level::INFO>("Set Power Cap Enable", 421 entry("POWERCAPENABLE=%u", requestData->powerLimitAction)); 422 423 *data_len = 0; 424 return IPMI_CC_OK; 425 } 426 427 ipmi_ret_t getAssetTag(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 428 ipmi_response_t response, ipmi_data_len_t data_len, 429 ipmi_context_t) 430 { 431 auto requestData = 432 reinterpret_cast<const dcmi::GetAssetTagRequest*>(request); 433 std::vector<uint8_t> outPayload(sizeof(dcmi::GetAssetTagResponse)); 434 auto responseData = 435 reinterpret_cast<dcmi::GetAssetTagResponse*>(outPayload.data()); 436 437 // Verify offset to read and number of bytes to read are not exceeding the 438 // range. 439 if ((requestData->offset > dcmi::assetTagMaxOffset) || 440 (requestData->bytes > dcmi::maxBytes) || 441 ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize)) 442 { 443 *data_len = 0; 444 return IPMI_CC_PARM_OUT_OF_RANGE; 445 } 446 447 std::string assetTag; 448 449 try 450 { 451 assetTag = dcmi::readAssetTag(); 452 } 453 catch (const InternalFailure& e) 454 { 455 *data_len = 0; 456 return IPMI_CC_UNSPECIFIED_ERROR; 457 } 458 459 // Return if the asset tag is not populated. 460 if (!assetTag.size()) 461 { 462 responseData->tagLength = 0; 463 memcpy(response, outPayload.data(), outPayload.size()); 464 *data_len = outPayload.size(); 465 return IPMI_CC_OK; 466 } 467 468 // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to suit 469 // Get Asset Tag command. 470 if (assetTag.size() > dcmi::assetTagMaxSize) 471 { 472 assetTag.resize(dcmi::assetTagMaxSize); 473 } 474 475 // If the requested offset is beyond the asset tag size. 476 if (requestData->offset >= assetTag.size()) 477 { 478 *data_len = 0; 479 return IPMI_CC_PARM_OUT_OF_RANGE; 480 } 481 482 auto returnData = assetTag.substr(requestData->offset, requestData->bytes); 483 484 responseData->tagLength = assetTag.size(); 485 486 memcpy(response, outPayload.data(), outPayload.size()); 487 memcpy(static_cast<uint8_t*>(response) + outPayload.size(), 488 returnData.data(), returnData.size()); 489 *data_len = outPayload.size() + returnData.size(); 490 491 return IPMI_CC_OK; 492 } 493 494 ipmi_ret_t setAssetTag(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 495 ipmi_response_t response, ipmi_data_len_t data_len, 496 ipmi_context_t) 497 { 498 auto requestData = 499 reinterpret_cast<const dcmi::SetAssetTagRequest*>(request); 500 std::vector<uint8_t> outPayload(sizeof(dcmi::SetAssetTagResponse)); 501 auto responseData = 502 reinterpret_cast<dcmi::SetAssetTagResponse*>(outPayload.data()); 503 504 // Verify offset to read and number of bytes to read are not exceeding the 505 // range. 506 if ((requestData->offset > dcmi::assetTagMaxOffset) || 507 (requestData->bytes > dcmi::maxBytes) || 508 ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize)) 509 { 510 *data_len = 0; 511 return IPMI_CC_PARM_OUT_OF_RANGE; 512 } 513 514 std::string assetTag; 515 516 try 517 { 518 assetTag = dcmi::readAssetTag(); 519 520 if (requestData->offset > assetTag.size()) 521 { 522 *data_len = 0; 523 return IPMI_CC_PARM_OUT_OF_RANGE; 524 } 525 526 assetTag.replace(requestData->offset, 527 assetTag.size() - requestData->offset, 528 static_cast<const char*>(request) + 529 sizeof(dcmi::SetAssetTagRequest), 530 requestData->bytes); 531 532 dcmi::writeAssetTag(assetTag); 533 534 responseData->tagLength = assetTag.size(); 535 memcpy(response, outPayload.data(), outPayload.size()); 536 *data_len = outPayload.size(); 537 538 return IPMI_CC_OK; 539 } 540 catch (const InternalFailure& e) 541 { 542 *data_len = 0; 543 return IPMI_CC_UNSPECIFIED_ERROR; 544 } 545 } 546 547 ipmi_ret_t getMgmntCtrlIdStr(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 548 ipmi_response_t response, ipmi_data_len_t data_len, 549 ipmi_context_t) 550 { 551 auto requestData = 552 reinterpret_cast<const dcmi::GetMgmntCtrlIdStrRequest*>(request); 553 auto responseData = 554 reinterpret_cast<dcmi::GetMgmntCtrlIdStrResponse*>(response); 555 std::string hostName; 556 557 *data_len = 0; 558 559 if (requestData->bytes > dcmi::maxBytes || 560 requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen) 561 { 562 return IPMI_CC_INVALID_FIELD_REQUEST; 563 } 564 565 try 566 { 567 hostName = dcmi::getHostName(); 568 } 569 catch (const InternalFailure& e) 570 { 571 return IPMI_CC_UNSPECIFIED_ERROR; 572 } 573 574 if (requestData->offset > hostName.length()) 575 { 576 return IPMI_CC_PARM_OUT_OF_RANGE; 577 } 578 auto responseStr = hostName.substr(requestData->offset, requestData->bytes); 579 auto responseStrLen = std::min(static_cast<std::size_t>(requestData->bytes), 580 responseStr.length() + 1); 581 responseData->strLen = hostName.length(); 582 std::copy(begin(responseStr), end(responseStr), responseData->data); 583 584 *data_len = sizeof(*responseData) + responseStrLen; 585 return IPMI_CC_OK; 586 } 587 588 ipmi_ret_t setMgmntCtrlIdStr(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 589 ipmi_response_t response, ipmi_data_len_t data_len, 590 ipmi_context_t) 591 { 592 static std::array<char, dcmi::maxCtrlIdStrLen + 1> newCtrlIdStr; 593 594 auto requestData = 595 reinterpret_cast<const dcmi::SetMgmntCtrlIdStrRequest*>(request); 596 auto responseData = 597 reinterpret_cast<dcmi::SetMgmntCtrlIdStrResponse*>(response); 598 599 *data_len = 0; 600 601 if (requestData->bytes > dcmi::maxBytes || 602 requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen + 1 || 603 (requestData->offset + requestData->bytes == 604 dcmi::maxCtrlIdStrLen + 1 && 605 requestData->data[requestData->bytes - 1] != '\0')) 606 { 607 return IPMI_CC_INVALID_FIELD_REQUEST; 608 } 609 610 try 611 { 612 /* if there is no old value and offset is not 0 */ 613 if (newCtrlIdStr[0] == '\0' && requestData->offset != 0) 614 { 615 /* read old ctrlIdStr */ 616 auto hostName = dcmi::getHostName(); 617 hostName.resize(dcmi::maxCtrlIdStrLen); 618 std::copy(begin(hostName), end(hostName), begin(newCtrlIdStr)); 619 newCtrlIdStr[hostName.length()] = '\0'; 620 } 621 622 /* replace part of string and mark byte after the last as \0 */ 623 auto restStrIter = 624 std::copy_n(requestData->data, requestData->bytes, 625 begin(newCtrlIdStr) + requestData->offset); 626 /* if the last written byte is not 64th - add '\0' */ 627 if (requestData->offset + requestData->bytes <= dcmi::maxCtrlIdStrLen) 628 { 629 *restStrIter = '\0'; 630 } 631 632 /* if input data contains '\0' whole string is sent - update hostname */ 633 auto it = std::find(requestData->data, 634 requestData->data + requestData->bytes, '\0'); 635 if (it != requestData->data + requestData->bytes) 636 { 637 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 638 ipmi::setDbusProperty(bus, dcmi::networkServiceName, 639 dcmi::networkConfigObj, 640 dcmi::networkConfigIntf, dcmi::hostNameProp, 641 std::string(newCtrlIdStr.data())); 642 } 643 } 644 catch (const InternalFailure& e) 645 { 646 *data_len = 0; 647 return IPMI_CC_UNSPECIFIED_ERROR; 648 } 649 650 responseData->offset = requestData->offset + requestData->bytes; 651 *data_len = sizeof(*responseData); 652 return IPMI_CC_OK; 653 } 654 655 // List of the capabilities under each parameter 656 dcmi::DCMICaps dcmiCaps = { 657 // Supported DCMI Capabilities 658 {dcmi::DCMICapParameters::SUPPORTED_DCMI_CAPS, 659 {3, 660 {{"PowerManagement", 2, 0, 1}, 661 {"OOBSecondaryLan", 3, 2, 1}, 662 {"SerialTMODE", 3, 1, 1}, 663 {"InBandSystemInterfaceChannel", 3, 0, 1}}}}, 664 // Mandatory Platform Attributes 665 {dcmi::DCMICapParameters::MANDATORY_PLAT_ATTRIBUTES, 666 {5, 667 {{"SELAutoRollOver", 1, 15, 1}, 668 {"FlushEntireSELUponRollOver", 1, 14, 1}, 669 {"RecordLevelSELFlushUponRollOver", 1, 13, 1}, 670 {"NumberOfSELEntries", 1, 0, 12}, 671 {"TempMonitoringSamplingFreq", 5, 0, 8}}}}, 672 // Optional Platform Attributes 673 {dcmi::DCMICapParameters::OPTIONAL_PLAT_ATTRIBUTES, 674 {2, 675 {{"PowerMgmtDeviceSlaveAddress", 1, 1, 7}, 676 {"BMCChannelNumber", 2, 4, 4}, 677 {"DeviceRivision", 2, 0, 4}}}}, 678 // Manageability Access Attributes 679 {dcmi::DCMICapParameters::MANAGEABILITY_ACCESS_ATTRIBUTES, 680 {3, 681 {{"MandatoryPrimaryLanOOBSupport", 1, 0, 8}, 682 {"OptionalSecondaryLanOOBSupport", 2, 0, 8}, 683 {"OptionalSerialOOBMTMODECapability", 3, 0, 8}}}}}; 684 685 ipmi_ret_t getDCMICapabilities(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 686 ipmi_response_t response, 687 ipmi_data_len_t data_len, ipmi_context_t) 688 { 689 690 std::ifstream dcmiCapFile(dcmi::gDCMICapabilitiesConfig); 691 if (!dcmiCapFile.is_open()) 692 { 693 log<level::ERR>("DCMI Capabilities file not found"); 694 return IPMI_CC_UNSPECIFIED_ERROR; 695 } 696 697 auto data = nlohmann::json::parse(dcmiCapFile, nullptr, false); 698 if (data.is_discarded()) 699 { 700 log<level::ERR>("DCMI Capabilities JSON parser failure"); 701 return IPMI_CC_UNSPECIFIED_ERROR; 702 } 703 704 auto requestData = 705 reinterpret_cast<const dcmi::GetDCMICapRequest*>(request); 706 707 // get list of capabilities in a parameter 708 auto caps = 709 dcmiCaps.find(static_cast<dcmi::DCMICapParameters>(requestData->param)); 710 if (caps == dcmiCaps.end()) 711 { 712 log<level::ERR>("Invalid input parameter"); 713 return IPMI_CC_INVALID_FIELD_REQUEST; 714 } 715 716 auto responseData = reinterpret_cast<dcmi::GetDCMICapResponse*>(response); 717 718 // For each capabilities in a parameter fill the data from 719 // the json file based on the capability name. 720 for (auto cap : caps->second.capList) 721 { 722 // If the data is beyond first byte boundary, insert in a 723 // 16bit pattern for example number of SEL entries are represented 724 // in 12bits. 725 if ((cap.length + cap.position) > dcmi::gByteBitSize) 726 { 727 uint16_t val = data.value(cap.name.c_str(), 0); 728 // According to DCMI spec v1.5, max number of SEL entries is 729 // 4096, but bit 12b of DCMI capabilities Mandatory Platform 730 // Attributes field is reserved and therefore we can use only 731 // the provided 12 bits with maximum value of 4095. 732 // We're playing safe here by applying the mask 733 // to ensure that provided value will fit into 12 bits. 734 if (cap.length > dcmi::gByteBitSize) 735 { 736 val &= dcmi::gMaxSELEntriesMask; 737 } 738 val <<= cap.position; 739 responseData->data[cap.bytePosition - 1] |= 740 static_cast<uint8_t>(val); 741 responseData->data[cap.bytePosition] |= val >> dcmi::gByteBitSize; 742 } 743 else 744 { 745 responseData->data[cap.bytePosition - 1] |= 746 data.value(cap.name.c_str(), 0) << cap.position; 747 } 748 } 749 750 responseData->major = DCMI_SPEC_MAJOR_VERSION; 751 responseData->minor = DCMI_SPEC_MINOR_VERSION; 752 responseData->paramRevision = DCMI_PARAMETER_REVISION; 753 *data_len = sizeof(*responseData) + caps->second.size; 754 755 return IPMI_CC_OK; 756 } 757 758 namespace dcmi 759 { 760 namespace temp_readings 761 { 762 763 Temperature readTemp(const std::string& dbusService, 764 const std::string& dbusPath) 765 { 766 // Read the temperature value from d-bus object. Need some conversion. 767 // As per the interface xyz.openbmc_project.Sensor.Value, the temperature 768 // is an double and in degrees C. It needs to be scaled by using the 769 // formula Value * 10^Scale. The ipmi spec has the temperature as a uint8_t, 770 // with a separate single bit for the sign. 771 772 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 773 auto result = ipmi::getAllDbusProperties( 774 bus, dbusService, dbusPath, "xyz.openbmc_project.Sensor.Value"); 775 auto temperature = 776 std::visit(ipmi::VariantToDoubleVisitor(), result.at("Value")); 777 double absTemp = std::abs(temperature); 778 779 auto findFactor = result.find("Scale"); 780 double factor = 0.0; 781 if (findFactor != result.end()) 782 { 783 factor = std::visit(ipmi::VariantToDoubleVisitor(), findFactor->second); 784 } 785 double scale = std::pow(10, factor); 786 787 auto tempDegrees = absTemp * scale; 788 // Max absolute temp as per ipmi spec is 128. 789 if (tempDegrees > maxTemp) 790 { 791 tempDegrees = maxTemp; 792 } 793 794 return std::make_tuple(static_cast<uint8_t>(tempDegrees), 795 (temperature < 0)); 796 } 797 798 std::tuple<Response, NumInstances> read(const std::string& type, 799 uint8_t instance) 800 { 801 Response response{}; 802 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 803 804 if (!instance) 805 { 806 log<level::ERR>("Expected non-zero instance"); 807 elog<InternalFailure>(); 808 } 809 810 auto data = parseJSONConfig(gDCMISensorsConfig); 811 static const std::vector<Json> empty{}; 812 std::vector<Json> readings = data.value(type, empty); 813 size_t numInstances = readings.size(); 814 for (const auto& j : readings) 815 { 816 uint8_t instanceNum = j.value("instance", 0); 817 // Not the instance we're interested in 818 if (instanceNum != instance) 819 { 820 continue; 821 } 822 823 std::string path = j.value("dbus", ""); 824 std::string service; 825 try 826 { 827 service = 828 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path); 829 } 830 catch (const std::exception& e) 831 { 832 log<level::DEBUG>(e.what()); 833 return std::make_tuple(response, numInstances); 834 } 835 836 response.instance = instance; 837 uint8_t temp{}; 838 bool sign{}; 839 std::tie(temp, sign) = readTemp(service, path); 840 response.temperature = temp; 841 response.sign = sign; 842 843 // Found the instance we're interested in 844 break; 845 } 846 847 if (numInstances > maxInstances) 848 { 849 numInstances = maxInstances; 850 } 851 return std::make_tuple(response, numInstances); 852 } 853 854 std::tuple<ResponseList, NumInstances> readAll(const std::string& type, 855 uint8_t instanceStart) 856 { 857 ResponseList response{}; 858 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 859 860 size_t numInstances = 0; 861 auto data = parseJSONConfig(gDCMISensorsConfig); 862 static const std::vector<Json> empty{}; 863 std::vector<Json> readings = data.value(type, empty); 864 numInstances = readings.size(); 865 for (const auto& j : readings) 866 { 867 try 868 { 869 // Max of 8 response data sets 870 if (response.size() == maxDataSets) 871 { 872 break; 873 } 874 875 uint8_t instanceNum = j.value("instance", 0); 876 // Not in the instance range we're interested in 877 if (instanceNum < instanceStart) 878 { 879 continue; 880 } 881 882 std::string path = j.value("dbus", ""); 883 auto service = 884 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path); 885 886 Response r{}; 887 r.instance = instanceNum; 888 uint8_t temp{}; 889 bool sign{}; 890 std::tie(temp, sign) = readTemp(service, path); 891 r.temperature = temp; 892 r.sign = sign; 893 response.push_back(r); 894 } 895 catch (const std::exception& e) 896 { 897 log<level::DEBUG>(e.what()); 898 continue; 899 } 900 } 901 902 if (numInstances > maxInstances) 903 { 904 numInstances = maxInstances; 905 } 906 return std::make_tuple(response, numInstances); 907 } 908 909 } // namespace temp_readings 910 } // namespace dcmi 911 912 ipmi_ret_t getTempReadings(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 913 ipmi_response_t response, ipmi_data_len_t data_len, 914 ipmi_context_t) 915 { 916 auto requestData = 917 reinterpret_cast<const dcmi::GetTempReadingsRequest*>(request); 918 auto responseData = 919 reinterpret_cast<dcmi::GetTempReadingsResponseHdr*>(response); 920 921 if (*data_len != sizeof(dcmi::GetTempReadingsRequest)) 922 { 923 log<level::ERR>("Malformed request data", 924 entry("DATA_SIZE=%d", *data_len)); 925 return IPMI_CC_REQ_DATA_LEN_INVALID; 926 } 927 *data_len = 0; 928 929 auto it = dcmi::entityIdToName.find(requestData->entityId); 930 if (it == dcmi::entityIdToName.end()) 931 { 932 log<level::ERR>("Unknown Entity ID", 933 entry("ENTITY_ID=%d", requestData->entityId)); 934 return IPMI_CC_INVALID_FIELD_REQUEST; 935 } 936 937 if (requestData->sensorType != dcmi::temperatureSensorType) 938 { 939 log<level::ERR>("Invalid sensor type", 940 entry("SENSOR_TYPE=%d", requestData->sensorType)); 941 return IPMI_CC_INVALID_FIELD_REQUEST; 942 } 943 944 dcmi::temp_readings::ResponseList temps{}; 945 try 946 { 947 if (!requestData->entityInstance) 948 { 949 // Read all instances 950 std::tie(temps, responseData->numInstances) = 951 dcmi::temp_readings::readAll(it->second, 952 requestData->instanceStart); 953 } 954 else 955 { 956 // Read one instance 957 temps.resize(1); 958 std::tie(temps[0], responseData->numInstances) = 959 dcmi::temp_readings::read(it->second, 960 requestData->entityInstance); 961 } 962 responseData->numDataSets = temps.size(); 963 } 964 catch (const InternalFailure& e) 965 { 966 return IPMI_CC_UNSPECIFIED_ERROR; 967 } 968 969 size_t payloadSize = temps.size() * sizeof(dcmi::temp_readings::Response); 970 if (!temps.empty()) 971 { 972 memcpy(responseData + 1, // copy payload right after the response header 973 temps.data(), payloadSize); 974 } 975 *data_len = sizeof(dcmi::GetTempReadingsResponseHdr) + payloadSize; 976 977 return IPMI_CC_OK; 978 } 979 980 int64_t getPowerReading(sdbusplus::bus::bus& bus) 981 { 982 std::ifstream sensorFile(POWER_READING_SENSOR); 983 std::string objectPath; 984 if (!sensorFile.is_open()) 985 { 986 log<level::ERR>("Power reading configuration file not found", 987 entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR)); 988 elog<InternalFailure>(); 989 } 990 991 auto data = nlohmann::json::parse(sensorFile, nullptr, false); 992 if (data.is_discarded()) 993 { 994 log<level::ERR>("Error in parsing configuration file", 995 entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR)); 996 elog<InternalFailure>(); 997 } 998 999 objectPath = data.value("path", ""); 1000 if (objectPath.empty()) 1001 { 1002 log<level::ERR>("Power sensor D-Bus object path is empty", 1003 entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR)); 1004 elog<InternalFailure>(); 1005 } 1006 1007 // Return default value if failed to read from D-Bus object 1008 int64_t power = 0; 1009 try 1010 { 1011 auto service = ipmi::getService(bus, SENSOR_VALUE_INTF, objectPath); 1012 1013 // Read the sensor value and scale properties 1014 auto value = ipmi::getDbusProperty( 1015 bus, service, objectPath, SENSOR_VALUE_INTF, SENSOR_VALUE_PROP); 1016 power = std::get<double>(value); 1017 } 1018 catch (const std::exception& e) 1019 { 1020 log<level::ERR>("Failure to read power value from D-Bus object", 1021 entry("OBJECT_PATH=%s", objectPath.c_str()), 1022 entry("INTERFACE=%s", SENSOR_VALUE_INTF)); 1023 } 1024 return power; 1025 } 1026 1027 ipmi_ret_t setDCMIConfParams(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 1028 ipmi_response_t, ipmi_data_len_t data_len, 1029 ipmi_context_t) 1030 { 1031 auto requestData = 1032 reinterpret_cast<const dcmi::SetConfParamsRequest*>(request); 1033 1034 if (*data_len < DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE || 1035 *data_len > DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE) 1036 { 1037 log<level::ERR>("Invalid Requested Packet size", 1038 entry("PACKET SIZE=%d", *data_len)); 1039 *data_len = 0; 1040 return IPMI_CC_INVALID_FIELD_REQUEST; 1041 } 1042 *data_len = 0; 1043 1044 try 1045 { 1046 // Take action based on the Parameter Selector 1047 switch ( 1048 static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect)) 1049 { 1050 case dcmi::DCMIConfigParameters::ActivateDHCP: 1051 1052 if ((requestData->data[0] & DCMI_ACTIVATE_DHCP_MASK) && 1053 dcmi::getDHCPEnabled()) 1054 { 1055 // When these conditions are met we have to trigger DHCP 1056 // protocol restart using the latest parameter settings, but 1057 // as per n/w manager design, each time when we update n/w 1058 // parameters, n/w service is restarted. So we no need to 1059 // take any action in this case. 1060 } 1061 break; 1062 1063 case dcmi::DCMIConfigParameters::DiscoveryConfig: 1064 1065 if (requestData->data[0] & DCMI_OPTION_12_MASK) 1066 { 1067 dcmi::setDHCPOption(DHCP_OPT12_ENABLED, true); 1068 } 1069 else 1070 { 1071 dcmi::setDHCPOption(DHCP_OPT12_ENABLED, false); 1072 } 1073 1074 // Systemd-networkd doesn't support Random Back off 1075 if (requestData->data[0] & DCMI_RAND_BACK_OFF_MASK) 1076 { 1077 return IPMI_CC_INVALID; 1078 } 1079 break; 1080 // Systemd-networkd doesn't allow to configure DHCP timigs 1081 case dcmi::DCMIConfigParameters::DHCPTiming1: 1082 case dcmi::DCMIConfigParameters::DHCPTiming2: 1083 case dcmi::DCMIConfigParameters::DHCPTiming3: 1084 default: 1085 return IPMI_CC_INVALID; 1086 } 1087 } 1088 catch (const std::exception& e) 1089 { 1090 log<level::ERR>(e.what()); 1091 return IPMI_CC_UNSPECIFIED_ERROR; 1092 } 1093 return IPMI_CC_OK; 1094 } 1095 1096 ipmi_ret_t getDCMIConfParams(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 1097 ipmi_response_t response, ipmi_data_len_t data_len, 1098 ipmi_context_t) 1099 { 1100 1101 auto requestData = 1102 reinterpret_cast<const dcmi::GetConfParamsRequest*>(request); 1103 auto responseData = 1104 reinterpret_cast<dcmi::GetConfParamsResponse*>(response); 1105 1106 responseData->data[0] = 0x00; 1107 1108 if (*data_len != sizeof(dcmi::GetConfParamsRequest)) 1109 { 1110 log<level::ERR>("Invalid Requested Packet size", 1111 entry("PACKET SIZE=%d", *data_len)); 1112 return IPMI_CC_INVALID_FIELD_REQUEST; 1113 } 1114 1115 *data_len = 0; 1116 1117 try 1118 { 1119 // Take action based on the Parameter Selector 1120 switch ( 1121 static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect)) 1122 { 1123 case dcmi::DCMIConfigParameters::ActivateDHCP: 1124 responseData->data[0] = DCMI_ACTIVATE_DHCP_REPLY; 1125 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1; 1126 break; 1127 case dcmi::DCMIConfigParameters::DiscoveryConfig: 1128 if (dcmi::getDHCPOption(DHCP_OPT12_ENABLED)) 1129 { 1130 responseData->data[0] |= DCMI_OPTION_12_MASK; 1131 } 1132 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1; 1133 break; 1134 // Get below values from Systemd-networkd source code 1135 case dcmi::DCMIConfigParameters::DHCPTiming1: 1136 responseData->data[0] = DHCP_TIMING1; 1137 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1; 1138 break; 1139 case dcmi::DCMIConfigParameters::DHCPTiming2: 1140 responseData->data[0] = DHCP_TIMING2_LOWER; 1141 responseData->data[1] = DHCP_TIMING2_UPPER; 1142 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2; 1143 break; 1144 case dcmi::DCMIConfigParameters::DHCPTiming3: 1145 responseData->data[0] = DHCP_TIMING3_LOWER; 1146 responseData->data[1] = DHCP_TIMING3_UPPER; 1147 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2; 1148 break; 1149 default: 1150 *data_len = 0; 1151 return IPMI_CC_INVALID; 1152 } 1153 } 1154 catch (const std::exception& e) 1155 { 1156 log<level::ERR>(e.what()); 1157 return IPMI_CC_UNSPECIFIED_ERROR; 1158 } 1159 1160 responseData->major = DCMI_SPEC_MAJOR_VERSION; 1161 responseData->minor = DCMI_SPEC_MINOR_VERSION; 1162 responseData->paramRevision = DCMI_CONFIG_PARAMETER_REVISION; 1163 1164 return IPMI_CC_OK; 1165 } 1166 1167 ipmi_ret_t getPowerReading(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, 1168 ipmi_response_t response, ipmi_data_len_t data_len, 1169 ipmi_context_t) 1170 { 1171 *data_len = 0; 1172 if (!dcmi::isDCMIPowerMgmtSupported()) 1173 { 1174 log<level::ERR>("DCMI Power management is unsupported!"); 1175 return IPMI_CC_INVALID; 1176 } 1177 1178 ipmi_ret_t rc = IPMI_CC_OK; 1179 auto responseData = 1180 reinterpret_cast<dcmi::GetPowerReadingResponse*>(response); 1181 1182 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 1183 int64_t power = 0; 1184 try 1185 { 1186 power = getPowerReading(bus); 1187 } 1188 catch (const InternalFailure& e) 1189 { 1190 log<level::ERR>("Error in reading power sensor value", 1191 entry("INTERFACE=%s", SENSOR_VALUE_INTF), 1192 entry("PROPERTY=%s", SENSOR_VALUE_PROP)); 1193 return IPMI_CC_UNSPECIFIED_ERROR; 1194 } 1195 1196 // TODO: openbmc/openbmc#2819 1197 // Minimum, Maximum, Average power, TimeFrame, TimeStamp, 1198 // PowerReadingState readings need to be populated 1199 // after Telemetry changes. 1200 uint16_t totalPower = static_cast<uint16_t>(power); 1201 responseData->currentPower = totalPower; 1202 responseData->minimumPower = totalPower; 1203 responseData->maximumPower = totalPower; 1204 responseData->averagePower = totalPower; 1205 1206 *data_len = sizeof(*responseData); 1207 return rc; 1208 } 1209 1210 namespace dcmi 1211 { 1212 namespace sensor_info 1213 { 1214 1215 Response createFromJson(const Json& config) 1216 { 1217 Response response{}; 1218 uint16_t recordId = config.value("record_id", 0); 1219 response.recordIdLsb = recordId & 0xFF; 1220 response.recordIdMsb = (recordId >> 8) & 0xFF; 1221 return response; 1222 } 1223 1224 std::tuple<Response, NumInstances> read(const std::string& type, 1225 uint8_t instance, const Json& config) 1226 { 1227 Response response{}; 1228 1229 if (!instance) 1230 { 1231 log<level::ERR>("Expected non-zero instance"); 1232 elog<InternalFailure>(); 1233 } 1234 1235 static const std::vector<Json> empty{}; 1236 std::vector<Json> readings = config.value(type, empty); 1237 size_t numInstances = readings.size(); 1238 for (const auto& reading : readings) 1239 { 1240 uint8_t instanceNum = reading.value("instance", 0); 1241 // Not the instance we're interested in 1242 if (instanceNum != instance) 1243 { 1244 continue; 1245 } 1246 1247 response = createFromJson(reading); 1248 1249 // Found the instance we're interested in 1250 break; 1251 } 1252 1253 if (numInstances > maxInstances) 1254 { 1255 log<level::DEBUG>("Trimming IPMI num instances", 1256 entry("NUM_INSTANCES=%d", numInstances)); 1257 numInstances = maxInstances; 1258 } 1259 return std::make_tuple(response, numInstances); 1260 } 1261 1262 std::tuple<ResponseList, NumInstances> 1263 readAll(const std::string& type, uint8_t instanceStart, const Json& config) 1264 { 1265 ResponseList responses{}; 1266 1267 size_t numInstances = 0; 1268 static const std::vector<Json> empty{}; 1269 std::vector<Json> readings = config.value(type, empty); 1270 numInstances = readings.size(); 1271 for (const auto& reading : readings) 1272 { 1273 try 1274 { 1275 // Max of 8 records 1276 if (responses.size() == maxRecords) 1277 { 1278 break; 1279 } 1280 1281 uint8_t instanceNum = reading.value("instance", 0); 1282 // Not in the instance range we're interested in 1283 if (instanceNum < instanceStart) 1284 { 1285 continue; 1286 } 1287 1288 Response response = createFromJson(reading); 1289 responses.push_back(response); 1290 } 1291 catch (const std::exception& e) 1292 { 1293 log<level::DEBUG>(e.what()); 1294 continue; 1295 } 1296 } 1297 1298 if (numInstances > maxInstances) 1299 { 1300 log<level::DEBUG>("Trimming IPMI num instances", 1301 entry("NUM_INSTANCES=%d", numInstances)); 1302 numInstances = maxInstances; 1303 } 1304 return std::make_tuple(responses, numInstances); 1305 } 1306 1307 } // namespace sensor_info 1308 } // namespace dcmi 1309 1310 ipmi_ret_t getSensorInfo(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, 1311 ipmi_response_t response, ipmi_data_len_t data_len, 1312 ipmi_context_t) 1313 { 1314 auto requestData = 1315 reinterpret_cast<const dcmi::GetSensorInfoRequest*>(request); 1316 auto responseData = 1317 reinterpret_cast<dcmi::GetSensorInfoResponseHdr*>(response); 1318 1319 if (*data_len != sizeof(dcmi::GetSensorInfoRequest)) 1320 { 1321 log<level::ERR>("Malformed request data", 1322 entry("DATA_SIZE=%d", *data_len)); 1323 return IPMI_CC_REQ_DATA_LEN_INVALID; 1324 } 1325 *data_len = 0; 1326 1327 auto it = dcmi::entityIdToName.find(requestData->entityId); 1328 if (it == dcmi::entityIdToName.end()) 1329 { 1330 log<level::ERR>("Unknown Entity ID", 1331 entry("ENTITY_ID=%d", requestData->entityId)); 1332 return IPMI_CC_INVALID_FIELD_REQUEST; 1333 } 1334 1335 if (requestData->sensorType != dcmi::temperatureSensorType) 1336 { 1337 log<level::ERR>("Invalid sensor type", 1338 entry("SENSOR_TYPE=%d", requestData->sensorType)); 1339 return IPMI_CC_INVALID_FIELD_REQUEST; 1340 } 1341 1342 dcmi::sensor_info::ResponseList sensors{}; 1343 static dcmi::Json config{}; 1344 static bool parsed = false; 1345 1346 try 1347 { 1348 if (!parsed) 1349 { 1350 config = dcmi::parseJSONConfig(dcmi::gDCMISensorsConfig); 1351 parsed = true; 1352 } 1353 1354 if (!requestData->entityInstance) 1355 { 1356 // Read all instances 1357 std::tie(sensors, responseData->numInstances) = 1358 dcmi::sensor_info::readAll(it->second, 1359 requestData->instanceStart, config); 1360 } 1361 else 1362 { 1363 // Read one instance 1364 sensors.resize(1); 1365 std::tie(sensors[0], responseData->numInstances) = 1366 dcmi::sensor_info::read(it->second, requestData->entityInstance, 1367 config); 1368 } 1369 responseData->numRecords = sensors.size(); 1370 } 1371 catch (const InternalFailure& e) 1372 { 1373 return IPMI_CC_UNSPECIFIED_ERROR; 1374 } 1375 1376 size_t payloadSize = sensors.size() * sizeof(dcmi::sensor_info::Response); 1377 if (!sensors.empty()) 1378 { 1379 memcpy(responseData + 1, // copy payload right after the response header 1380 sensors.data(), payloadSize); 1381 } 1382 *data_len = sizeof(dcmi::GetSensorInfoResponseHdr) + payloadSize; 1383 1384 return IPMI_CC_OK; 1385 } 1386 1387 void register_netfn_dcmi_functions() 1388 { 1389 // <Get Power Limit> 1390 1391 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT, NULL, 1392 getPowerLimit, PRIVILEGE_USER); 1393 1394 // <Set Power Limit> 1395 1396 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT, NULL, 1397 setPowerLimit, PRIVILEGE_OPERATOR); 1398 1399 // <Activate/Deactivate Power Limit> 1400 1401 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT, 1402 NULL, applyPowerLimit, PRIVILEGE_OPERATOR); 1403 1404 // <Get Asset Tag> 1405 1406 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG, NULL, 1407 getAssetTag, PRIVILEGE_USER); 1408 1409 // <Set Asset Tag> 1410 1411 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG, NULL, 1412 setAssetTag, PRIVILEGE_OPERATOR); 1413 1414 // <Get Management Controller Identifier String> 1415 1416 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_MGMNT_CTRL_ID_STR, 1417 NULL, getMgmntCtrlIdStr, PRIVILEGE_USER); 1418 1419 // <Set Management Controller Identifier String> 1420 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR, 1421 NULL, setMgmntCtrlIdStr, PRIVILEGE_ADMIN); 1422 1423 // <Get DCMI capabilities> 1424 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CAPABILITIES, 1425 NULL, getDCMICapabilities, PRIVILEGE_USER); 1426 1427 // <Get Temperature Readings> 1428 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_TEMP_READINGS, 1429 NULL, getTempReadings, PRIVILEGE_USER); 1430 1431 // <Get Power Reading> 1432 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_READING, 1433 NULL, getPowerReading, PRIVILEGE_USER); 1434 1435 // <Get Sensor Info> 1436 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_SENSOR_INFO, NULL, 1437 getSensorInfo, PRIVILEGE_USER); 1438 1439 // <Get DCMI Configuration Parameters> 1440 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CONF_PARAMS, NULL, 1441 getDCMIConfParams, PRIVILEGE_USER); 1442 1443 // <Set DCMI Configuration Parameters> 1444 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_CONF_PARAMS, NULL, 1445 setDCMIConfParams, PRIVILEGE_ADMIN); 1446 1447 return; 1448 } 1449 // 956379 1450