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