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