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