DCMI: Implement Get DCMI capabilities info command.

This commit adds ipmi changes for supporting the
get DCMI capabilities command

Resolves openbmc/openbmc#2618

Change-Id: I7d0e7c132f4a8d459351e025fa2bfca9fcf1340b
Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
diff --git a/dcmihandler.cpp b/dcmihandler.cpp
index ab3aa58..aa9f14e 100644
--- a/dcmihandler.cpp
+++ b/dcmihandler.cpp
@@ -7,6 +7,9 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdint.h>
+#include <fstream>
+#include <bitset>
+#include "nlohmann/json.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
 
 using namespace phosphor::logging;
@@ -21,6 +24,11 @@
 constexpr auto POWER_CAP_PROP = "PowerCap";
 constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable";
 
+constexpr auto DCMI_PARAMETER_REVISION = 2;
+constexpr auto DCMI_SPEC_MAJOR_VERSION = 1;
+constexpr auto DCMI_SPEC_MINOR_VERSION = 5;
+constexpr auto DCMI_CAP_JSON_FILE = "/usr/share/ipmi-providers/dcmi_cap.json";
+
 using namespace phosphor::logging;
 
 namespace dcmi
@@ -603,6 +611,126 @@
     return IPMI_CC_OK;
 }
 
+//List of the capabilities under each parameter
+dcmi::DCMICaps dcmiCaps =
+{
+//Supported DCMI Capabilities
+    {
+        dcmi::DCMICapParameters::SUPPORTED_DCMI_CAPS,
+        {
+            3, {{"PowerManagement", 2, 0, 1},
+                {"OOBSecondaryLan", 3, 2, 1},
+                {"SerialTMODE", 3, 1, 1},
+                {"InBandSystemInterfaceChannel", 3, 0, 1}
+            }
+        }
+    },
+//Mandatory Platform Attributes
+    {
+        dcmi::DCMICapParameters::MANDATORY_PLAT_ATTRIBUTES,
+        {
+            5, {{"SELAutoRollOver", 1, 15, 1},
+                {"FlushEntireSELUponRollOver", 1, 14, 1},
+                {"RecordLevelSELFlushUponRollOver", 1, 13, 1},
+                {"NumberOfSELEntries", 1, 0, 12},
+                {"TempMonitoringSamplingFreq", 5, 0, 8}
+            }
+        }
+    },
+//Optional Platform Attributes
+    {
+        dcmi::DCMICapParameters::OPTIONAL_PLAT_ATTRIBUTES,
+        {
+            2, {{"PowerMgmtDeviceSlaveAddress", 1, 1, 7},
+                {"BMCChannelNumber", 2, 4, 4},
+                {"DeviceRivision", 2, 0, 4}
+            }
+        }
+    },
+//Manageability Access Attributes
+    {
+        dcmi::DCMICapParameters::MANAGEABILITY_ACCESS_ATTRIBUTES,
+        {
+            3, {{"MandatoryPrimaryLanOOBSupport", 1, 0, 8},
+                {"OptionalSecondaryLanOOBSupport", 2, 0, 8},
+                {"OptionalSerialOOBMTMODECapability", 3, 0, 8}
+            }
+        }
+    }
+};
+
+ipmi_ret_t getDCMICapabilities(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)
+{
+
+    std::ifstream dcmiCapFile(DCMI_CAP_JSON_FILE);
+    if (!dcmiCapFile.is_open())
+    {
+        log<level::ERR>("DCMI Capabilities file not found");
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    auto data = nlohmann::json::parse(dcmiCapFile, nullptr, false);
+    if (data.is_discarded())
+    {
+        log<level::ERR>("DCMI Capabilities JSON parser failure");
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    auto requestData = reinterpret_cast<const dcmi::GetDCMICapRequest*>
+                       (request);
+
+    //get list of capabilities in a parameter
+    auto caps =
+        dcmiCaps.find(static_cast<dcmi::DCMICapParameters>(requestData->param));
+    if (caps == dcmiCaps.end())
+    {
+        log<level::ERR>("Invalid input parameter");
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    if (requestData->groupID != dcmi::groupExtId)
+    {
+        *data_len = 0;
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    auto responseData = reinterpret_cast<dcmi::GetDCMICapResponse*>
+                        (response);
+
+    //For each capabilities in a parameter fill the data from
+    //the json file based on the capability name.
+    for (auto cap : caps->second.capList)
+    {
+        //If the data is beyond first byte boundary, insert in a
+        //16bit pattern for example number of SEL entries are represented
+        //in 12bits.
+        if ((cap.length + cap.position) > 8)
+        {
+            //Read the value corresponding to capability name and assign to
+            //16bit bitset.
+            std::bitset<16> val(data.value(cap.name.c_str(), 0));
+            val <<= cap.position;
+            reinterpret_cast<uint16_t*>(responseData->data)[
+                (cap.bytePosition - 1) / sizeof(uint16_t)] |= val.to_ulong();
+        }
+        else
+        {
+            responseData->data[cap.bytePosition - 1] |=
+                data.value(cap.name.c_str(), 0) << cap.position;
+        }
+    }
+
+    responseData->groupID = dcmi::groupExtId;
+    responseData->major = DCMI_SPEC_MAJOR_VERSION;
+    responseData->minor = DCMI_SPEC_MINOR_VERSION;
+    responseData->paramRevision = DCMI_PARAMETER_REVISION;
+    *data_len = sizeof(*responseData) + caps->second.size;
+
+    return IPMI_CC_OK;
+}
+
 void register_netfn_dcmi_functions()
 {
     // <Get Power Limit>
@@ -653,6 +781,9 @@
     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR,
         NULL, setMgmntCtrlIdStr, PRIVILEGE_ADMIN);
 
+    // <Get DCMI capabilities>
+    ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CAPABILITIES,
+        NULL, getDCMICapabilities, PRIVILEGE_USER);
     return;
 }
 // 956379
diff --git a/dcmihandler.hpp b/dcmihandler.hpp
index 91707f3..288e4d7 100644
--- a/dcmihandler.hpp
+++ b/dcmihandler.hpp
@@ -12,6 +12,7 @@
 enum Commands
 {
     // Get capability bits
+    GET_CAPABILITIES = 0x01,
     GET_POWER_LIMIT = 0x03,
     SET_POWER_LIMIT = 0x04,
     APPLY_POWER_LIMIT = 0x05,
@@ -258,6 +259,67 @@
     uint8_t offset;             //!< Last Offset Written.
 } __attribute__((packed));
 
+/** @enum DCMICapParameters
+ *
+ * DCMI Capability parameters
+ */
+enum class DCMICapParameters
+{
+    SUPPORTED_DCMI_CAPS = 0x01,             //!< Supported DCMI Capabilities
+    MANDATORY_PLAT_ATTRIBUTES = 0x02,       //!< Mandatory Platform Attributes
+    OPTIONAL_PLAT_ATTRIBUTES = 0x03,        //!< Optional Platform Attributes
+    MANAGEABILITY_ACCESS_ATTRIBUTES = 0x04, //!< Manageability Access Attributes
+};
+
+/** @struct GetDCMICapRequest
+ *
+ *  DCMI payload for Get capabilities cmd request.
+ */
+struct GetDCMICapRequest
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint8_t param;              //!< Capability parameter selector.
+} __attribute__((packed));
+
+/** @struct GetDCMICapRequest
+ *
+ *  DCMI payload for Get capabilities cmd response.
+ */
+struct GetDCMICapResponse
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint8_t major;              //!< DCMI Specification Conformance - major ver
+    uint8_t minor;              //!< DCMI Specification Conformance - minor ver
+    uint8_t paramRevision;      //!< Parameter Revision = 02h
+    uint8_t data[];             //!< Capability array
+} __attribute__((packed));
+
+/** @struct DCMICap
+ *
+ *  DCMI capabilities protocol info.
+ */
+struct DCMICap
+{
+    std::string name;           //!< Name of DCMI capability.
+    uint8_t bytePosition;       //!< Starting byte number from DCMI spec.
+    uint8_t position;           //!< bit position from the DCMI spec.
+    uint8_t length;             //!< Length of the value from DCMI spec.
+};
+
+using DCMICapList = std::vector<DCMICap>;
+
+/** @struct DCMICapEntry
+ *
+ *  DCMI capabilities list and size for each parameter.
+ */
+struct DCMICapEntry
+{
+    uint8_t size;               //!< Size of capability array in bytes.
+    DCMICapList capList;        //!< List of capabilities for a parameter.
+};
+
+using DCMICaps = std::map<DCMICapParameters, DCMICapEntry>;
+
 } // namespace dcmi
 
 #endif
diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf
index 68e3233..941800a 100644
--- a/host-ipmid-whitelist.conf
+++ b/host-ipmid-whitelist.conf
@@ -27,5 +27,6 @@
 0x0A:0x49    //<Storage>:<Set SEL Time>
 0x0C:0x02    //<Transport>:<Get LAN Configuration Parameters>
 0x2C:0x00    //<Group Extension>:<Group Extension Command>
+0x2C:0x01    //<Group Extension>:<Get DCMI Capabilities>
 0x2C:0x03    //<Group Extension>:<Get Power Limit>
 0x2C:0x06    //<Group Extension>:<Get Asset Tag>