#include "dcmihandler.hpp" #include "host-ipmid/ipmid-api.h" #include #include #include #include "utils.hpp" #include #include #include #include "xyz/openbmc_project/Common/error.hpp" using namespace phosphor::logging; using InternalFailure = sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; void register_netfn_dcmi_functions() __attribute__((constructor)); constexpr auto PCAP_PATH = "/xyz/openbmc_project/control/host0/power_cap"; constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap"; constexpr auto POWER_CAP_PROP = "PowerCap"; constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable"; using namespace phosphor::logging; namespace dcmi { uint32_t getPcap(sdbusplus::bus::bus& bus) { auto settingService = ipmi::getService(bus, PCAP_INTERFACE,PCAP_PATH); auto method = bus.new_method_call(settingService.c_str(), PCAP_PATH, "org.freedesktop.DBus.Properties", "Get"); method.append(PCAP_INTERFACE, POWER_CAP_PROP); auto reply = bus.call(method); if (reply.is_method_error()) { log("Error in getPcap prop"); elog(); } sdbusplus::message::variant pcap; reply.read(pcap); return pcap.get(); } bool getPcapEnabled(sdbusplus::bus::bus& bus) { auto settingService = ipmi::getService(bus, PCAP_INTERFACE,PCAP_PATH); auto method = bus.new_method_call(settingService.c_str(), PCAP_PATH, "org.freedesktop.DBus.Properties", "Get"); method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP); auto reply = bus.call(method); if (reply.is_method_error()) { log("Error in getPcapEnabled prop"); elog(); } sdbusplus::message::variant pcapEnabled; reply.read(pcapEnabled); return pcapEnabled.get(); } void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap) { auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH); auto method = bus.new_method_call(service.c_str(), PCAP_PATH, "org.freedesktop.DBus.Properties", "Set"); method.append(PCAP_INTERFACE, POWER_CAP_PROP); method.append(sdbusplus::message::variant(powerCap)); auto reply = bus.call(method); if (reply.is_method_error()) { log("Error in setPcap property"); elog(); } } void setPcapEnable(sdbusplus::bus::bus& bus, bool enabled) { auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH); auto method = bus.new_method_call(service.c_str(), PCAP_PATH, "org.freedesktop.DBus.Properties", "Set"); method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP); method.append(sdbusplus::message::variant(enabled)); auto reply = bus.call(method); if (reply.is_method_error()) { log("Error in setPcapEnabled property"); elog(); } } void readAssetTagObjectTree(dcmi::assettag::ObjectTree& objectTree) { static constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper"; static constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper"; static constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper"; static constexpr auto inventoryRoot = "/xyz/openbmc_project/inventory/"; sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; auto depth = 0; auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath, mapperIface, "GetSubTree"); mapperCall.append(inventoryRoot); mapperCall.append(depth); mapperCall.append(std::vector({dcmi::assetTagIntf})); auto mapperReply = bus.call(mapperCall); if (mapperReply.is_method_error()) { log("Error in mapper call"); elog(); } mapperReply.read(objectTree); if (objectTree.empty()) { log("AssetTag property is not populated"); elog(); } } std::string readAssetTag() { sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; dcmi::assettag::ObjectTree objectTree; // Read the object tree with the inventory root to figure out the object // that has implemented the Asset tag interface. readAssetTagObjectTree(objectTree); auto method = bus.new_method_call( (objectTree.begin()->second.begin()->first).c_str(), (objectTree.begin()->first).c_str(), dcmi::propIntf, "Get"); method.append(dcmi::assetTagIntf); method.append(dcmi::assetTagProp); auto reply = bus.call(method); if (reply.is_method_error()) { log("Error in reading asset tag"); elog(); } sdbusplus::message::variant assetTag; reply.read(assetTag); return assetTag.get(); } void writeAssetTag(const std::string& assetTag) { sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; dcmi::assettag::ObjectTree objectTree; // Read the object tree with the inventory root to figure out the object // that has implemented the Asset tag interface. readAssetTagObjectTree(objectTree); auto method = bus.new_method_call( (objectTree.begin()->second.begin()->first).c_str(), (objectTree.begin()->first).c_str(), dcmi::propIntf, "Set"); method.append(dcmi::assetTagIntf); method.append(dcmi::assetTagProp); method.append(sdbusplus::message::variant(assetTag)); auto reply = bus.call(method); if (reply.is_method_error()) { log("Error in writing asset tag"); elog(); } } std::string getHostName(void) { sdbusplus::bus::bus bus{ ipmid_get_sd_bus_connection() }; auto service = ipmi::getService(bus, networkConfigIntf, networkConfigObj); auto value = ipmi::getDbusProperty(bus, service, networkConfigObj, networkConfigIntf, hostNameProp); return value.get(); } } // namespace dcmi ipmi_ret_t getPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { auto requestData = reinterpret_cast (request); std::vector outPayload(sizeof(dcmi::GetPowerLimitResponse)); auto responseData = reinterpret_cast (outPayload.data()); if (requestData->groupID != dcmi::groupExtId) { *data_len = 0; return IPMI_CC_INVALID_FIELD_REQUEST; } sdbusplus::bus::bus sdbus {ipmid_get_sd_bus_connection()}; uint32_t pcapValue = 0; bool pcapEnable = false; try { pcapValue = dcmi::getPcap(sdbus); pcapEnable = dcmi::getPcapEnabled(sdbus); } catch (InternalFailure& e) { *data_len = 0; return IPMI_CC_UNSPECIFIED_ERROR; } responseData->groupID = dcmi::groupExtId; /* * Exception action if power limit is exceeded and cannot be controlled * with the correction time limit is hardcoded to Hard Power Off system * and log event to SEL. */ constexpr auto exception = 0x01; responseData->exceptionAction = exception; responseData->powerLimit = static_cast(pcapValue); /* * Correction time limit and Statistics sampling period is currently not * populated. */ *data_len = outPayload.size(); memcpy(response, outPayload.data(), *data_len); if (pcapEnable) { return IPMI_CC_OK; } else { return IPMI_DCMI_CC_NO_ACTIVE_POWER_LIMIT; } } ipmi_ret_t setPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { auto requestData = reinterpret_cast (request); std::vector outPayload(sizeof(dcmi::SetPowerLimitResponse)); auto responseData = reinterpret_cast (outPayload.data()); if (requestData->groupID != dcmi::groupExtId) { *data_len = 0; return IPMI_CC_INVALID_FIELD_REQUEST; } sdbusplus::bus::bus sdbus {ipmid_get_sd_bus_connection()}; // Only process the power limit requested in watts. try { dcmi::setPcap(sdbus, requestData->powerLimit); } catch (InternalFailure& e) { *data_len = 0; return IPMI_CC_UNSPECIFIED_ERROR; } log("Set Power Cap", entry("POWERCAP=%u", requestData->powerLimit)); responseData->groupID = dcmi::groupExtId; memcpy(response, outPayload.data(), outPayload.size()); *data_len = outPayload.size(); return IPMI_CC_OK; } ipmi_ret_t applyPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { auto requestData = reinterpret_cast (request); std::vector outPayload(sizeof(dcmi::ApplyPowerLimitResponse)); auto responseData = reinterpret_cast (outPayload.data()); if (requestData->groupID != dcmi::groupExtId) { *data_len = 0; return IPMI_CC_INVALID_FIELD_REQUEST; } sdbusplus::bus::bus sdbus {ipmid_get_sd_bus_connection()}; try { dcmi::setPcapEnable(sdbus, static_cast(requestData->powerLimitAction)); } catch (InternalFailure& e) { *data_len = 0; return IPMI_CC_UNSPECIFIED_ERROR; } log("Set Power Cap Enable", entry("POWERCAPENABLE=%u", requestData->powerLimitAction)); responseData->groupID = dcmi::groupExtId; memcpy(response, outPayload.data(), outPayload.size()); *data_len = outPayload.size(); return IPMI_CC_OK; } ipmi_ret_t getAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { auto requestData = reinterpret_cast (request); std::vector outPayload(sizeof(dcmi::GetAssetTagResponse)); auto responseData = reinterpret_cast (outPayload.data()); if (requestData->groupID != dcmi::groupExtId) { *data_len = 0; return IPMI_CC_INVALID_FIELD_REQUEST; } // Verify offset to read and number of bytes to read are not exceeding the // range. if ((requestData->offset > dcmi::assetTagMaxOffset) || (requestData->bytes > dcmi::maxBytes) || ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize)) { *data_len = 0; return IPMI_CC_PARM_OUT_OF_RANGE; } std::string assetTag; try { assetTag = dcmi::readAssetTag(); } catch (InternalFailure& e) { *data_len = 0; return IPMI_CC_UNSPECIFIED_ERROR; } responseData->groupID = dcmi::groupExtId; // Return if the asset tag is not populated. if (!assetTag.size()) { responseData->tagLength = 0; memcpy(response, outPayload.data(), outPayload.size()); *data_len = outPayload.size(); return IPMI_CC_OK; } // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to suit // Get Asset Tag command. if (assetTag.size() > dcmi::assetTagMaxSize) { assetTag.resize(dcmi::assetTagMaxSize); } // If the requested offset is beyond the asset tag size. if (requestData->offset >= assetTag.size()) { *data_len = 0; return IPMI_CC_PARM_OUT_OF_RANGE; } auto returnData = assetTag.substr(requestData->offset, requestData->bytes); responseData->tagLength = assetTag.size(); memcpy(response, outPayload.data(), outPayload.size()); memcpy(static_cast(response) + outPayload.size(), returnData.data(), returnData.size()); *data_len = outPayload.size() + returnData.size(); return IPMI_CC_OK; } ipmi_ret_t setAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { auto requestData = reinterpret_cast (request); std::vector outPayload(sizeof(dcmi::SetAssetTagResponse)); auto responseData = reinterpret_cast (outPayload.data()); if (requestData->groupID != dcmi::groupExtId) { *data_len = 0; return IPMI_CC_INVALID_FIELD_REQUEST; } // Verify offset to read and number of bytes to read are not exceeding the // range. if ((requestData->offset > dcmi::assetTagMaxOffset) || (requestData->bytes > dcmi::maxBytes) || ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize)) { *data_len = 0; return IPMI_CC_PARM_OUT_OF_RANGE; } std::string assetTag; try { assetTag = dcmi::readAssetTag(); if (requestData->offset > assetTag.size()) { *data_len = 0; return IPMI_CC_PARM_OUT_OF_RANGE; } assetTag.replace(requestData->offset, assetTag.size() - requestData->offset, static_cast(request) + sizeof(dcmi::SetAssetTagRequest), requestData->bytes); dcmi::writeAssetTag(assetTag); responseData->groupID = dcmi::groupExtId; responseData->tagLength = assetTag.size(); memcpy(response, outPayload.data(), outPayload.size()); *data_len = outPayload.size(); return IPMI_CC_OK; } catch (InternalFailure& e) { *data_len = 0; return IPMI_CC_UNSPECIFIED_ERROR; } } ipmi_ret_t getMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { auto requestData = reinterpret_cast (request); auto responseData = reinterpret_cast (response); std::string hostName; *data_len = 0; if (requestData->groupID != dcmi::groupExtId || requestData->bytes > dcmi::maxBytes || requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen) { return IPMI_CC_INVALID_FIELD_REQUEST; } try { hostName = dcmi::getHostName(); } catch (InternalFailure& e) { return IPMI_CC_UNSPECIFIED_ERROR; } if (requestData->offset > hostName.length()) { return IPMI_CC_PARM_OUT_OF_RANGE; } auto responseStr = hostName.substr(requestData->offset, requestData->bytes); auto responseStrLen = std::min(static_cast(requestData->bytes), responseStr.length() + 1); responseData->groupID = dcmi::groupExtId; responseData->strLen = hostName.length(); std::copy(begin(responseStr), end(responseStr), responseData->data); *data_len = sizeof(*responseData) + responseStrLen; return IPMI_CC_OK; } ipmi_ret_t setMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { static std::array newCtrlIdStr; auto requestData = reinterpret_cast (request); auto responseData = reinterpret_cast (response); *data_len = 0; if (requestData->groupID != dcmi::groupExtId || requestData->bytes > dcmi::maxBytes || requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen + 1 || (requestData->offset + requestData->bytes == dcmi::maxCtrlIdStrLen + 1 && requestData->data[requestData->bytes - 1] != '\0')) { return IPMI_CC_INVALID_FIELD_REQUEST; } try { /* if there is no old value and offset is not 0 */ if (newCtrlIdStr[0] == '\0' && requestData->offset != 0) { /* read old ctrlIdStr */ auto hostName = dcmi::getHostName(); hostName.resize(dcmi::maxCtrlIdStrLen); std::copy(begin(hostName), end(hostName), begin(newCtrlIdStr)); newCtrlIdStr[hostName.length()] = '\0'; } /* replace part of string and mark byte after the last as \0 */ auto restStrIter = std::copy_n(requestData->data, requestData->bytes, begin(newCtrlIdStr) + requestData->offset); /* if the last written byte is not 64th - add '\0' */ if (requestData->offset + requestData->bytes <= dcmi::maxCtrlIdStrLen) { *restStrIter = '\0'; } /* if input data contains '\0' whole string is sent - update hostname */ auto it = std::find(requestData->data, requestData->data + requestData->bytes, '\0'); if (it != requestData->data + requestData->bytes) { sdbusplus::bus::bus bus{ ipmid_get_sd_bus_connection() }; ipmi::setDbusProperty(bus, dcmi::networkServiceName, dcmi::networkConfigObj, dcmi::networkConfigIntf, dcmi::hostNameProp, std::string(newCtrlIdStr.data())); } } catch (InternalFailure& e) { *data_len = 0; return IPMI_CC_UNSPECIFIED_ERROR; } responseData->groupID = dcmi::groupExtId; responseData->offset = requestData->offset + requestData->bytes; *data_len = sizeof(*responseData); return IPMI_CC_OK; } void register_netfn_dcmi_functions() { // printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT); ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT, NULL, getPowerLimit, PRIVILEGE_USER); // printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT); ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT, NULL, setPowerLimit, PRIVILEGE_OPERATOR); // printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT); ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT, NULL, applyPowerLimit, PRIVILEGE_OPERATOR); // printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG); ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG, NULL, getAssetTag, PRIVILEGE_USER); // printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG); ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG, NULL, setAssetTag, PRIVILEGE_OPERATOR); // printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_GRPEXT, dcmi::Commands::GET_MGMNT_CTRL_ID_STR); ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_MGMNT_CTRL_ID_STR, NULL, getMgmntCtrlIdStr, PRIVILEGE_USER); // printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR); ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR, NULL, setMgmntCtrlIdStr, PRIVILEGE_ADMIN); return; } // 956379