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