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