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