Add DCMI Get/Set Management Controller Id String

Resolves openbmc/openbmc#1855
Change-Id: I878c7bcb1ea8b46cd3e932b1cbb2290fe612f652
Signed-off-by: Vladislav Vovchenko <vlad.vovchenko93@gmail.com>
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/dcmihandler.cpp b/dcmihandler.cpp
index 5df6559..806ddd0 100644
--- a/dcmihandler.cpp
+++ b/dcmihandler.cpp
@@ -207,6 +207,17 @@
     }
 }
 
+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<std::string>();
+}
+
 } // namespace dcmi
 
 ipmi_ret_t getPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
@@ -483,6 +494,115 @@
     }
 }
 
+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<const dcmi::GetMgmntCtrlIdStrRequest *>
+        (request);
+    auto responseData = reinterpret_cast<dcmi::GetMgmntCtrlIdStrResponse *>
+        (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<std::size_t>(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<char, dcmi::maxCtrlIdStrLen + 1> newCtrlIdStr;
+
+    auto requestData = reinterpret_cast<const dcmi::SetMgmntCtrlIdStrRequest *>
+        (request);
+    auto responseData = reinterpret_cast<dcmi::SetMgmntCtrlIdStrResponse *>
+        (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()
 {
     // <Get Power Limit>
@@ -519,6 +639,20 @@
 
     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG,
                            NULL, setAssetTag, PRIVILEGE_OPERATOR);
+
+    // <Get Managment Controller Identifier String>
+    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);
+
+    // <Set Management Controller Identifier String>
+    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
diff --git a/dcmihandler.hpp b/dcmihandler.hpp
index 212e9ac..91707f3 100644
--- a/dcmihandler.hpp
+++ b/dcmihandler.hpp
@@ -17,13 +17,20 @@
     APPLY_POWER_LIMIT = 0x05,
     GET_ASSET_TAG = 0x06,
     SET_ASSET_TAG = 0x08,
+    GET_MGMNT_CTRL_ID_STR = 0x09,
+    SET_MGMNT_CTRL_ID_STR = 0x0A,
 };
 
-
 static constexpr auto propIntf = "org.freedesktop.DBus.Properties";
 static constexpr auto assetTagIntf =
         "xyz.openbmc_project.Inventory.Decorator.AssetTag";
 static constexpr auto assetTagProp = "AssetTag";
+static constexpr auto networkServiceName = "xyz.openbmc_project.Network";
+static constexpr auto networkConfigObj =
+        "/xyz/openbmc_project/network/config";
+static constexpr auto networkConfigIntf =
+        "xyz.openbmc_project.Network.SystemConfiguration";
+static constexpr auto hostNameProp = "HostName";
 
 namespace assettag
 {
@@ -40,6 +47,7 @@
 static constexpr auto assetTagMaxOffset = 62;
 static constexpr auto assetTagMaxSize = 63;
 static constexpr auto maxBytes = 16;
+static constexpr size_t maxCtrlIdStrLen = 63;
 
 /** @struct GetAssetTagRequest
  *
@@ -206,6 +214,50 @@
     uint8_t groupID;            //!< Group extension identification.
 } __attribute__((packed));
 
+/** @struct GetMgmntCtrlIdStrRequest
+ *
+ *  DCMI payload for Get Management Controller Identifier String cmd request.
+ */
+struct GetMgmntCtrlIdStrRequest
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint8_t offset;             //!< Offset to read.
+    uint8_t bytes;              //!< Number of bytes to read.
+} __attribute__((packed));
+
+/** @struct GetMgmntCtrlIdStrResponse
+ *
+ *  DCMI payload for Get Management Controller Identifier String cmd response.
+ */
+struct GetMgmntCtrlIdStrResponse
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint8_t strLen;             //!< ID string length.
+    char data[];                //!< ID string
+} __attribute__((packed));
+
+/** @struct SetMgmntCtrlIdStrRequest
+ *
+ *  DCMI payload for Set Management Controller Identifier String cmd request.
+ */
+struct SetMgmntCtrlIdStrRequest
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint8_t offset;             //!< Offset to write.
+    uint8_t bytes;              //!< Number of bytes to read.
+    char data[];                //!< ID string
+} __attribute__((packed));
+
+/** @struct GetMgmntCtrlIdStrResponse
+ *
+ *  DCMI payload for Get Management Controller Identifier String cmd response.
+ */
+struct SetMgmntCtrlIdStrResponse
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint8_t offset;             //!< Last Offset Written.
+} __attribute__((packed));
+
 } // namespace dcmi
 
 #endif