1 #include "config.h" 2 3 #include "dcmihandler.hpp" 4 5 #include "user_channel/channel_layer.hpp" 6 #include "utils.hpp" 7 8 #include <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 <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) > 8) 788 { 789 // Read the value corresponding to capability name and assign to 790 // 16bit bitset. 791 std::bitset<16> val(data.value(cap.name.c_str(), 0)); 792 val <<= cap.position; 793 reinterpret_cast<uint16_t*>( 794 responseData 795 ->data)[(cap.bytePosition - 1) / sizeof(uint16_t)] |= 796 val.to_ulong(); 797 } 798 else 799 { 800 responseData->data[cap.bytePosition - 1] |= 801 data.value(cap.name.c_str(), 0) << cap.position; 802 } 803 } 804 805 responseData->groupID = dcmi::groupExtId; 806 responseData->major = DCMI_SPEC_MAJOR_VERSION; 807 responseData->minor = DCMI_SPEC_MINOR_VERSION; 808 responseData->paramRevision = DCMI_PARAMETER_REVISION; 809 *data_len = sizeof(*responseData) + caps->second.size; 810 811 return IPMI_CC_OK; 812 } 813 814 namespace dcmi 815 { 816 namespace temp_readings 817 { 818 819 Temperature readTemp(const std::string& dbusService, 820 const std::string& dbusPath) 821 { 822 // Read the temperature value from d-bus object. Need some conversion. 823 // As per the interface xyz.openbmc_project.Sensor.Value, the temperature 824 // is an double and in degrees C. It needs to be scaled by using the 825 // formula Value * 10^Scale. The ipmi spec has the temperature as a uint8_t, 826 // with a separate single bit for the sign. 827 828 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 829 auto result = ipmi::getAllDbusProperties( 830 bus, dbusService, dbusPath, "xyz.openbmc_project.Sensor.Value"); 831 auto temperature = sdbusplus::message::variant_ns::visit( 832 ipmi::VariantToDoubleVisitor(), result.at("Value")); 833 double absTemp = std::abs(temperature); 834 835 auto findFactor = result.find("Scale"); 836 double factor = 0.0; 837 if (findFactor != result.end()) 838 { 839 factor = sdbusplus::message::variant_ns::visit( 840 ipmi::VariantToDoubleVisitor(), findFactor->second); 841 } 842 double scale = std::pow(10, factor); 843 844 auto tempDegrees = absTemp * scale; 845 // Max absolute temp as per ipmi spec is 128. 846 if (tempDegrees > maxTemp) 847 { 848 tempDegrees = maxTemp; 849 } 850 851 return std::make_tuple(static_cast<uint8_t>(tempDegrees), 852 (temperature < 0)); 853 } 854 855 std::tuple<Response, NumInstances> read(const std::string& type, 856 uint8_t instance) 857 { 858 Response response{}; 859 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 860 861 if (!instance) 862 { 863 log<level::ERR>("Expected non-zero instance"); 864 elog<InternalFailure>(); 865 } 866 867 auto data = parseJSONConfig(gDCMISensorsConfig); 868 static const std::vector<Json> empty{}; 869 std::vector<Json> readings = data.value(type, empty); 870 size_t numInstances = readings.size(); 871 for (const auto& j : readings) 872 { 873 uint8_t instanceNum = j.value("instance", 0); 874 // Not the instance we're interested in 875 if (instanceNum != instance) 876 { 877 continue; 878 } 879 880 std::string path = j.value("dbus", ""); 881 std::string service; 882 try 883 { 884 service = 885 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path); 886 } 887 catch (std::exception& e) 888 { 889 log<level::DEBUG>(e.what()); 890 return std::make_tuple(response, numInstances); 891 } 892 893 response.instance = instance; 894 uint8_t temp{}; 895 bool sign{}; 896 std::tie(temp, sign) = readTemp(service, path); 897 response.temperature = temp; 898 response.sign = sign; 899 900 // Found the instance we're interested in 901 break; 902 } 903 904 if (numInstances > maxInstances) 905 { 906 numInstances = maxInstances; 907 } 908 return std::make_tuple(response, numInstances); 909 } 910 911 std::tuple<ResponseList, NumInstances> readAll(const std::string& type, 912 uint8_t instanceStart) 913 { 914 ResponseList response{}; 915 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 916 917 size_t numInstances = 0; 918 auto data = parseJSONConfig(gDCMISensorsConfig); 919 static const std::vector<Json> empty{}; 920 std::vector<Json> readings = data.value(type, empty); 921 numInstances = readings.size(); 922 for (const auto& j : readings) 923 { 924 try 925 { 926 // Max of 8 response data sets 927 if (response.size() == maxDataSets) 928 { 929 break; 930 } 931 932 uint8_t instanceNum = j.value("instance", 0); 933 // Not in the instance range we're interested in 934 if (instanceNum < instanceStart) 935 { 936 continue; 937 } 938 939 std::string path = j.value("dbus", ""); 940 auto service = 941 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path); 942 943 Response r{}; 944 r.instance = instanceNum; 945 uint8_t temp{}; 946 bool sign{}; 947 std::tie(temp, sign) = readTemp(service, path); 948 r.temperature = temp; 949 r.sign = sign; 950 response.push_back(r); 951 } 952 catch (std::exception& e) 953 { 954 log<level::DEBUG>(e.what()); 955 continue; 956 } 957 } 958 959 if (numInstances > maxInstances) 960 { 961 numInstances = maxInstances; 962 } 963 return std::make_tuple(response, numInstances); 964 } 965 966 } // namespace temp_readings 967 } // namespace dcmi 968 969 ipmi_ret_t getTempReadings(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 970 ipmi_request_t request, ipmi_response_t response, 971 ipmi_data_len_t data_len, ipmi_context_t context) 972 { 973 auto requestData = 974 reinterpret_cast<const dcmi::GetTempReadingsRequest*>(request); 975 auto responseData = 976 reinterpret_cast<dcmi::GetTempReadingsResponseHdr*>(response); 977 978 if (*data_len != sizeof(dcmi::GetTempReadingsRequest)) 979 { 980 log<level::ERR>("Malformed request data", 981 entry("DATA_SIZE=%d", *data_len)); 982 return IPMI_CC_REQ_DATA_LEN_INVALID; 983 } 984 *data_len = 0; 985 986 auto it = dcmi::entityIdToName.find(requestData->entityId); 987 if (it == dcmi::entityIdToName.end()) 988 { 989 log<level::ERR>("Unknown Entity ID", 990 entry("ENTITY_ID=%d", requestData->entityId)); 991 return IPMI_CC_INVALID_FIELD_REQUEST; 992 } 993 994 if (requestData->groupID != dcmi::groupExtId) 995 { 996 log<level::ERR>("Invalid Group ID", 997 entry("GROUP_ID=%d", requestData->groupID)); 998 return IPMI_CC_INVALID_FIELD_REQUEST; 999 } 1000 1001 if (requestData->sensorType != dcmi::temperatureSensorType) 1002 { 1003 log<level::ERR>("Invalid sensor type", 1004 entry("SENSOR_TYPE=%d", requestData->sensorType)); 1005 return IPMI_CC_INVALID_FIELD_REQUEST; 1006 } 1007 1008 dcmi::temp_readings::ResponseList temps{}; 1009 try 1010 { 1011 if (!requestData->entityInstance) 1012 { 1013 // Read all instances 1014 std::tie(temps, responseData->numInstances) = 1015 dcmi::temp_readings::readAll(it->second, 1016 requestData->instanceStart); 1017 } 1018 else 1019 { 1020 // Read one instance 1021 temps.resize(1); 1022 std::tie(temps[0], responseData->numInstances) = 1023 dcmi::temp_readings::read(it->second, 1024 requestData->entityInstance); 1025 } 1026 responseData->numDataSets = temps.size(); 1027 } 1028 catch (InternalFailure& e) 1029 { 1030 return IPMI_CC_UNSPECIFIED_ERROR; 1031 } 1032 1033 responseData->groupID = dcmi::groupExtId; 1034 size_t payloadSize = temps.size() * sizeof(dcmi::temp_readings::Response); 1035 if (!temps.empty()) 1036 { 1037 memcpy(responseData + 1, // copy payload right after the response header 1038 temps.data(), payloadSize); 1039 } 1040 *data_len = sizeof(dcmi::GetTempReadingsResponseHdr) + payloadSize; 1041 1042 return IPMI_CC_OK; 1043 } 1044 1045 int64_t getPowerReading(sdbusplus::bus::bus& bus) 1046 { 1047 std::ifstream sensorFile(POWER_READING_SENSOR); 1048 std::string objectPath; 1049 if (!sensorFile.is_open()) 1050 { 1051 log<level::ERR>("Power reading configuration file not found", 1052 entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR)); 1053 elog<InternalFailure>(); 1054 } 1055 1056 auto data = nlohmann::json::parse(sensorFile, nullptr, false); 1057 if (data.is_discarded()) 1058 { 1059 log<level::ERR>("Error in parsing configuration file", 1060 entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR)); 1061 elog<InternalFailure>(); 1062 } 1063 1064 objectPath = data.value("path", ""); 1065 if (objectPath.empty()) 1066 { 1067 log<level::ERR>("Power sensor D-Bus object path is empty", 1068 entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR)); 1069 elog<InternalFailure>(); 1070 } 1071 1072 // Return default value if failed to read from D-Bus object 1073 int64_t power = 0; 1074 try 1075 { 1076 auto service = ipmi::getService(bus, SENSOR_VALUE_INTF, objectPath); 1077 1078 // Read the sensor value and scale properties 1079 auto properties = ipmi::getAllDbusProperties(bus, service, objectPath, 1080 SENSOR_VALUE_INTF); 1081 auto value = variant_ns::get<int64_t>(properties[SENSOR_VALUE_PROP]); 1082 auto scale = variant_ns::get<int64_t>(properties[SENSOR_SCALE_PROP]); 1083 1084 // Power reading needs to be scaled with the Scale value using the 1085 // formula Value * 10^Scale. 1086 power = value * std::pow(10, scale); 1087 } 1088 catch (std::exception& e) 1089 { 1090 log<level::INFO>("Failure to read power value from D-Bus object", 1091 entry("OBJECT_PATH=%s", objectPath.c_str()), 1092 entry("INTERFACE=%s", SENSOR_VALUE_INTF)); 1093 } 1094 return power; 1095 } 1096 1097 ipmi_ret_t setDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1098 ipmi_request_t request, ipmi_response_t response, 1099 ipmi_data_len_t data_len, ipmi_context_t context) 1100 { 1101 auto requestData = 1102 reinterpret_cast<const dcmi::SetConfParamsRequest*>(request); 1103 auto responseData = 1104 reinterpret_cast<dcmi::SetConfParamsResponse*>(response); 1105 1106 if (requestData->groupID != dcmi::groupExtId || 1107 *data_len < DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE || 1108 *data_len > DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE) 1109 { 1110 log<level::ERR>("Invalid Group ID or Invalid Requested Packet size", 1111 entry("GROUP_ID=%d", requestData->groupID), 1112 entry("PACKET SIZE=%d", *data_len)); 1113 return IPMI_CC_INVALID_FIELD_REQUEST; 1114 } 1115 1116 *data_len = 0; 1117 1118 try 1119 { 1120 // Take action based on the Parameter Selector 1121 switch ( 1122 static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect)) 1123 { 1124 case dcmi::DCMIConfigParameters::ActivateDHCP: 1125 1126 if ((requestData->data[0] & DCMI_ACTIVATE_DHCP_MASK) && 1127 dcmi::getDHCPEnabled()) 1128 { 1129 // When these conditions are met we have to trigger DHCP 1130 // protocol restart using the latest parameter settings, but 1131 // as per n/w manager design, each time when we update n/w 1132 // parameters, n/w service is restarted. So we no need to 1133 // take any action in this case. 1134 } 1135 break; 1136 1137 case dcmi::DCMIConfigParameters::DiscoveryConfig: 1138 1139 if (requestData->data[0] & DCMI_OPTION_12_MASK) 1140 { 1141 dcmi::setDHCPOption(DHCP_OPT12_ENABLED, true); 1142 } 1143 else 1144 { 1145 dcmi::setDHCPOption(DHCP_OPT12_ENABLED, false); 1146 } 1147 1148 // Systemd-networkd doesn't support Random Back off 1149 if (requestData->data[0] & DCMI_RAND_BACK_OFF_MASK) 1150 { 1151 return IPMI_CC_INVALID; 1152 } 1153 break; 1154 // Systemd-networkd doesn't allow to configure DHCP timigs 1155 case dcmi::DCMIConfigParameters::DHCPTiming1: 1156 case dcmi::DCMIConfigParameters::DHCPTiming2: 1157 case dcmi::DCMIConfigParameters::DHCPTiming3: 1158 default: 1159 return IPMI_CC_INVALID; 1160 } 1161 } 1162 catch (std::exception& e) 1163 { 1164 log<level::ERR>(e.what()); 1165 return IPMI_CC_UNSPECIFIED_ERROR; 1166 } 1167 responseData->groupID = dcmi::groupExtId; 1168 *data_len = sizeof(dcmi::SetConfParamsResponse); 1169 1170 return IPMI_CC_OK; 1171 } 1172 1173 ipmi_ret_t getDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1174 ipmi_request_t request, ipmi_response_t response, 1175 ipmi_data_len_t data_len, ipmi_context_t context) 1176 { 1177 1178 auto requestData = 1179 reinterpret_cast<const dcmi::GetConfParamsRequest*>(request); 1180 auto responseData = 1181 reinterpret_cast<dcmi::GetConfParamsResponse*>(response); 1182 1183 responseData->data[0] = 0x00; 1184 1185 if (requestData->groupID != dcmi::groupExtId || 1186 *data_len != sizeof(dcmi::GetConfParamsRequest)) 1187 { 1188 log<level::ERR>("Invalid Group ID or Invalid Requested Packet size", 1189 entry("GROUP_ID=%d", requestData->groupID), 1190 entry("PACKET SIZE=%d", *data_len)); 1191 return IPMI_CC_INVALID_FIELD_REQUEST; 1192 } 1193 1194 *data_len = 0; 1195 1196 try 1197 { 1198 // Take action based on the Parameter Selector 1199 switch ( 1200 static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect)) 1201 { 1202 case dcmi::DCMIConfigParameters::ActivateDHCP: 1203 responseData->data[0] = DCMI_ACTIVATE_DHCP_REPLY; 1204 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1; 1205 break; 1206 case dcmi::DCMIConfigParameters::DiscoveryConfig: 1207 if (dcmi::getDHCPOption(DHCP_OPT12_ENABLED)) 1208 { 1209 responseData->data[0] |= DCMI_OPTION_12_MASK; 1210 } 1211 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1; 1212 break; 1213 // Get below values from Systemd-networkd source code 1214 case dcmi::DCMIConfigParameters::DHCPTiming1: 1215 responseData->data[0] = DHCP_TIMING1; 1216 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1; 1217 break; 1218 case dcmi::DCMIConfigParameters::DHCPTiming2: 1219 responseData->data[0] = DHCP_TIMING2_LOWER; 1220 responseData->data[1] = DHCP_TIMING2_UPPER; 1221 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2; 1222 break; 1223 case dcmi::DCMIConfigParameters::DHCPTiming3: 1224 responseData->data[0] = DHCP_TIMING3_LOWER; 1225 responseData->data[1] = DHCP_TIMING3_UPPER; 1226 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2; 1227 break; 1228 default: 1229 *data_len = 0; 1230 return IPMI_CC_INVALID; 1231 } 1232 } 1233 catch (std::exception& e) 1234 { 1235 log<level::ERR>(e.what()); 1236 return IPMI_CC_UNSPECIFIED_ERROR; 1237 } 1238 1239 responseData->groupID = dcmi::groupExtId; 1240 responseData->major = DCMI_SPEC_MAJOR_VERSION; 1241 responseData->minor = DCMI_SPEC_MINOR_VERSION; 1242 responseData->paramRevision = DCMI_CONFIG_PARAMETER_REVISION; 1243 1244 return IPMI_CC_OK; 1245 } 1246 1247 ipmi_ret_t getPowerReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1248 ipmi_request_t request, ipmi_response_t response, 1249 ipmi_data_len_t data_len, ipmi_context_t context) 1250 { 1251 if (!dcmi::isDCMIPowerMgmtSupported()) 1252 { 1253 *data_len = 0; 1254 log<level::ERR>("DCMI Power management is unsupported!"); 1255 return IPMI_CC_INVALID; 1256 } 1257 1258 ipmi_ret_t rc = IPMI_CC_OK; 1259 auto requestData = 1260 reinterpret_cast<const dcmi::GetPowerReadingRequest*>(request); 1261 auto responseData = 1262 reinterpret_cast<dcmi::GetPowerReadingResponse*>(response); 1263 1264 if (requestData->groupID != dcmi::groupExtId) 1265 { 1266 *data_len = 0; 1267 return IPMI_CC_INVALID_FIELD_REQUEST; 1268 } 1269 1270 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 1271 int64_t power = 0; 1272 try 1273 { 1274 power = getPowerReading(bus); 1275 } 1276 catch (InternalFailure& e) 1277 { 1278 log<level::ERR>("Error in reading power sensor value", 1279 entry("INTERFACE=%s", SENSOR_VALUE_INTF), 1280 entry("PROPERTY=%s", SENSOR_VALUE_PROP)); 1281 return IPMI_CC_UNSPECIFIED_ERROR; 1282 } 1283 responseData->groupID = dcmi::groupExtId; 1284 1285 // TODO: openbmc/openbmc#2819 1286 // Minimum, Maximum, Average power, TimeFrame, TimeStamp, 1287 // PowerReadingState readings need to be populated 1288 // after Telemetry changes. 1289 uint16_t totalPower = static_cast<uint16_t>(power); 1290 responseData->currentPower = totalPower; 1291 responseData->minimumPower = totalPower; 1292 responseData->maximumPower = totalPower; 1293 responseData->averagePower = totalPower; 1294 1295 *data_len = sizeof(*responseData); 1296 return rc; 1297 } 1298 1299 namespace dcmi 1300 { 1301 namespace sensor_info 1302 { 1303 1304 Response createFromJson(const Json& config) 1305 { 1306 Response response{}; 1307 uint16_t recordId = config.value("record_id", 0); 1308 response.recordIdLsb = recordId & 0xFF; 1309 response.recordIdMsb = (recordId >> 8) & 0xFF; 1310 return response; 1311 } 1312 1313 std::tuple<Response, NumInstances> read(const std::string& type, 1314 uint8_t instance, const Json& config) 1315 { 1316 Response response{}; 1317 1318 if (!instance) 1319 { 1320 log<level::ERR>("Expected non-zero instance"); 1321 elog<InternalFailure>(); 1322 } 1323 1324 static const std::vector<Json> empty{}; 1325 std::vector<Json> readings = config.value(type, empty); 1326 size_t numInstances = readings.size(); 1327 for (const auto& reading : readings) 1328 { 1329 uint8_t instanceNum = reading.value("instance", 0); 1330 // Not the instance we're interested in 1331 if (instanceNum != instance) 1332 { 1333 continue; 1334 } 1335 1336 response = createFromJson(reading); 1337 1338 // Found the instance we're interested in 1339 break; 1340 } 1341 1342 if (numInstances > maxInstances) 1343 { 1344 log<level::DEBUG>("Trimming IPMI num instances", 1345 entry("NUM_INSTANCES=%d", numInstances)); 1346 numInstances = maxInstances; 1347 } 1348 return std::make_tuple(response, numInstances); 1349 } 1350 1351 std::tuple<ResponseList, NumInstances> 1352 readAll(const std::string& type, uint8_t instanceStart, const Json& config) 1353 { 1354 ResponseList responses{}; 1355 1356 size_t numInstances = 0; 1357 static const std::vector<Json> empty{}; 1358 std::vector<Json> readings = config.value(type, empty); 1359 numInstances = readings.size(); 1360 for (const auto& reading : readings) 1361 { 1362 try 1363 { 1364 // Max of 8 records 1365 if (responses.size() == maxRecords) 1366 { 1367 break; 1368 } 1369 1370 uint8_t instanceNum = reading.value("instance", 0); 1371 // Not in the instance range we're interested in 1372 if (instanceNum < instanceStart) 1373 { 1374 continue; 1375 } 1376 1377 Response response = createFromJson(reading); 1378 responses.push_back(response); 1379 } 1380 catch (std::exception& e) 1381 { 1382 log<level::DEBUG>(e.what()); 1383 continue; 1384 } 1385 } 1386 1387 if (numInstances > maxInstances) 1388 { 1389 log<level::DEBUG>("Trimming IPMI num instances", 1390 entry("NUM_INSTANCES=%d", numInstances)); 1391 numInstances = maxInstances; 1392 } 1393 return std::make_tuple(responses, numInstances); 1394 } 1395 1396 } // namespace sensor_info 1397 } // namespace dcmi 1398 1399 ipmi_ret_t getSensorInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1400 ipmi_request_t request, ipmi_response_t response, 1401 ipmi_data_len_t data_len, ipmi_context_t context) 1402 { 1403 auto requestData = 1404 reinterpret_cast<const dcmi::GetSensorInfoRequest*>(request); 1405 auto responseData = 1406 reinterpret_cast<dcmi::GetSensorInfoResponseHdr*>(response); 1407 1408 if (*data_len != sizeof(dcmi::GetSensorInfoRequest)) 1409 { 1410 log<level::ERR>("Malformed request data", 1411 entry("DATA_SIZE=%d", *data_len)); 1412 return IPMI_CC_REQ_DATA_LEN_INVALID; 1413 } 1414 *data_len = 0; 1415 1416 auto it = dcmi::entityIdToName.find(requestData->entityId); 1417 if (it == dcmi::entityIdToName.end()) 1418 { 1419 log<level::ERR>("Unknown Entity ID", 1420 entry("ENTITY_ID=%d", requestData->entityId)); 1421 return IPMI_CC_INVALID_FIELD_REQUEST; 1422 } 1423 1424 if (requestData->groupID != dcmi::groupExtId) 1425 { 1426 log<level::ERR>("Invalid Group ID", 1427 entry("GROUP_ID=%d", requestData->groupID)); 1428 return IPMI_CC_INVALID_FIELD_REQUEST; 1429 } 1430 1431 if (requestData->sensorType != dcmi::temperatureSensorType) 1432 { 1433 log<level::ERR>("Invalid sensor type", 1434 entry("SENSOR_TYPE=%d", requestData->sensorType)); 1435 return IPMI_CC_INVALID_FIELD_REQUEST; 1436 } 1437 1438 dcmi::sensor_info::ResponseList sensors{}; 1439 static dcmi::Json config{}; 1440 static bool parsed = false; 1441 1442 try 1443 { 1444 if (!parsed) 1445 { 1446 config = dcmi::parseJSONConfig(dcmi::gDCMISensorsConfig); 1447 parsed = true; 1448 } 1449 1450 if (!requestData->entityInstance) 1451 { 1452 // Read all instances 1453 std::tie(sensors, responseData->numInstances) = 1454 dcmi::sensor_info::readAll(it->second, 1455 requestData->instanceStart, config); 1456 } 1457 else 1458 { 1459 // Read one instance 1460 sensors.resize(1); 1461 std::tie(sensors[0], responseData->numInstances) = 1462 dcmi::sensor_info::read(it->second, requestData->entityInstance, 1463 config); 1464 } 1465 responseData->numRecords = sensors.size(); 1466 } 1467 catch (InternalFailure& e) 1468 { 1469 return IPMI_CC_UNSPECIFIED_ERROR; 1470 } 1471 1472 responseData->groupID = dcmi::groupExtId; 1473 size_t payloadSize = sensors.size() * sizeof(dcmi::sensor_info::Response); 1474 if (!sensors.empty()) 1475 { 1476 memcpy(responseData + 1, // copy payload right after the response header 1477 sensors.data(), payloadSize); 1478 } 1479 *data_len = sizeof(dcmi::GetSensorInfoResponseHdr) + payloadSize; 1480 1481 return IPMI_CC_OK; 1482 } 1483 1484 void register_netfn_dcmi_functions() 1485 { 1486 // <Get Power Limit> 1487 1488 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT, NULL, 1489 getPowerLimit, PRIVILEGE_USER); 1490 1491 // <Set Power Limit> 1492 1493 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT, NULL, 1494 setPowerLimit, PRIVILEGE_OPERATOR); 1495 1496 // <Activate/Deactivate Power Limit> 1497 1498 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT, 1499 NULL, applyPowerLimit, PRIVILEGE_OPERATOR); 1500 1501 // <Get Asset Tag> 1502 1503 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG, NULL, 1504 getAssetTag, PRIVILEGE_USER); 1505 1506 // <Set Asset Tag> 1507 1508 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG, NULL, 1509 setAssetTag, PRIVILEGE_OPERATOR); 1510 1511 // <Get Management Controller Identifier String> 1512 1513 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_MGMNT_CTRL_ID_STR, 1514 NULL, getMgmntCtrlIdStr, PRIVILEGE_USER); 1515 1516 // <Set Management Controller Identifier String> 1517 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR, 1518 NULL, setMgmntCtrlIdStr, PRIVILEGE_ADMIN); 1519 1520 // <Get DCMI capabilities> 1521 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CAPABILITIES, 1522 NULL, getDCMICapabilities, PRIVILEGE_USER); 1523 1524 // <Get Temperature Readings> 1525 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_TEMP_READINGS, 1526 NULL, getTempReadings, PRIVILEGE_USER); 1527 1528 // <Get Power Reading> 1529 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_READING, 1530 NULL, getPowerReading, PRIVILEGE_USER); 1531 1532 // <Get Sensor Info> 1533 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_SENSOR_INFO, NULL, 1534 getSensorInfo, PRIVILEGE_USER); 1535 1536 // <Get DCMI Configuration Parameters> 1537 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CONF_PARAMS, NULL, 1538 getDCMIConfParams, PRIVILEGE_USER); 1539 1540 // <Set DCMI Configuration Parameters> 1541 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_CONF_PARAMS, NULL, 1542 setDCMIConfParams, PRIVILEGE_ADMIN); 1543 1544 return; 1545 } 1546 // 956379 1547