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