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