dcmi: Refactor get power limit command

Change-Id: I31755eaccb17c013fd2c1a3e43e7ec95a1947ba3
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/dcmihandler.cpp b/dcmihandler.cpp
index 9205207..2e0204d 100644
--- a/dcmihandler.cpp
+++ b/dcmihandler.cpp
@@ -23,6 +23,9 @@
 
 using namespace phosphor::logging;
 
+namespace dcmi
+{
+
 uint32_t getPcap(sdbusplus::bus::bus& bus)
 {
     auto settingService = ipmi::getService(bus,
@@ -39,14 +42,12 @@
     if (reply.is_method_error())
     {
         log<level::ERR>("Error in getPcap prop");
-        // TODO openbmc/openbmc#851 - Once available, throw returned error
-        // and return IPMI_CC_UNSPECIFIED_ERROR to caller
-        return 0;
+        elog<InternalFailure>();
     }
     sdbusplus::message::variant<uint32_t> pcap;
     reply.read(pcap);
 
-    return sdbusplus::message::variant_ns::get<uint32_t>(pcap);
+    return pcap.get<uint32_t>();
 }
 
 bool getPcapEnabled(sdbusplus::bus::bus& bus)
@@ -65,58 +66,14 @@
     if (reply.is_method_error())
     {
         log<level::ERR>("Error in getPcapEnabled prop");
-        // TODO openbmc/openbmc#851 - Once available, throw returned error
-        // and return IPMI_CC_UNSPECIFIED_ERROR to caller
-        return false;
+        elog<InternalFailure>();
     }
     sdbusplus::message::variant<bool> pcapEnabled;
     reply.read(pcapEnabled);
 
-    return sdbusplus::message::variant_ns::get<bool>(pcapEnabled);
+    return pcapEnabled.get<bool>();
 }
 
-ipmi_ret_t ipmi_dcmi_get_power_limit(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)
-{
-    // Default to no power cap enabled
-    ipmi_ret_t rc = IPMI_DCMI_CC_NO_ACTIVE_POWER_LIMIT;
-
-    // Get our sdbus object
-    sd_bus *bus = ipmid_get_sd_bus_connection();
-    sdbusplus::bus::bus sdbus {bus};
-
-    // Read our power cap settings
-    auto pcap = getPcap(sdbus);
-    auto pcapEnable = getPcapEnabled(sdbus);
-    if(pcapEnable)
-    {
-        // indicate power cap enabled with success return code
-        rc = IPMI_CC_OK;
-    }
-
-    uint8_t pcapBytes[2] = {0};
-    pcapBytes[1] = (pcap & 0xFF00) >> 8;
-    pcapBytes[0] = pcap & 0xFF;
-    // dcmi-v1-5-rev-spec.pdf 6.6.2.
-    uint8_t data_response[] = { 0xDC, 0x00, 0x00, 0x01, pcapBytes[0],
-                                pcapBytes[1], 0x00, 0x00, 0x00, 0x00, 0x00,
-                                0x00, 0x00, 0x01};
-
-
-    log<level::INFO>("IPMI DCMI POWER CAP INFO",
-                     entry("DCMI_PCAP=%u",pcap),
-                     entry("DCMI_PCAP_ENABLE=%u",pcapEnable));
-
-    memcpy(response, data_response, sizeof(data_response));
-    *data_len = sizeof(data_response);
-
-    return rc;
-}
-
-namespace dcmi
-{
-
 void readAssetTagObjectTree(dcmi::assettag::ObjectTree& objectTree)
 {
     static constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper";
@@ -210,6 +167,67 @@
 
 } // 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<const dcmi::GetPowerLimitRequest*>
+                   (request);
+    std::vector<uint8_t> outPayload(sizeof(dcmi::GetPowerLimitResponse));
+    auto responseData = reinterpret_cast<dcmi::GetPowerLimitResponse*>
+            (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<uint16_t>(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 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)
@@ -348,8 +366,8 @@
 void register_netfn_dcmi_functions()
 {
     // <Get Power Limit>
-    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_GRPEXT, IPMI_CMD_DCMI_GET_POWER);
-    ipmi_register_callback(NETFUN_GRPEXT, IPMI_CMD_DCMI_GET_POWER, NULL, ipmi_dcmi_get_power_limit,
+    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_GRPEXT, IPMI_CMD_DCMI_GET_POWER_LIMIT);
+    ipmi_register_callback(NETFUN_GRPEXT, IPMI_CMD_DCMI_GET_POWER_LIMIT, NULL, getPowerLimit,
                            PRIVILEGE_USER);
 
     // <Get Asset Tag>
diff --git a/dcmihandler.hpp b/dcmihandler.hpp
index de0d82f..8ebc660 100644
--- a/dcmihandler.hpp
+++ b/dcmihandler.hpp
@@ -4,12 +4,13 @@
 #include <map>
 #include <string>
 #include <vector>
+#include <sdbusplus/bus.hpp>
 
 // IPMI commands for net functions.
 enum ipmi_netfn_sen_cmds
 {
     // Get capability bits
-    IPMI_CMD_DCMI_GET_POWER = 0x03,
+    IPMI_CMD_DCMI_GET_POWER_LIMIT = 0x03,
     IPMI_CMD_DCMI_GET_ASSET_TAG = 0x06,
     IPMI_CMD_DCMI_SET_ASSET_TAG = 0x08,
 };
@@ -102,6 +103,48 @@
  */
 void writeAssetTag(const std::string& assetTag);
 
+/** @brief Read the current power cap value
+ *
+ *  @param[in] bus - dbus connection
+ *
+ *  @return On success return the power cap value.
+ */
+uint32_t getPcap(sdbusplus::bus::bus& bus);
+
+/** @brief Check if the power capping is enabled
+ *
+ *  @param[in] bus - dbus connection
+ *
+ *  @return true if the powerCap is enabled and false if the powercap
+ *          is disabled.
+ */
+bool getPcapEnabled(sdbusplus::bus::bus& bus);
+
+/** @struct GetPowerLimitRequest
+ *
+ *  DCMI payload for Get Power Limit command request.
+ */
+struct GetPowerLimitRequest
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint16_t reserved;          //!< Reserved
+} __attribute__((packed));
+
+/** @struct GetPowerLimitResponse
+ *
+ *  DCMI payload for Get Power Limit command response.
+ */
+struct GetPowerLimitResponse
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint16_t reserved;          //!< Reserved.
+    uint8_t exceptionAction;    //!< Exception action.
+    uint16_t powerLimit;        //!< Power limit requested in watts.
+    uint32_t correctionTime;    //!< Correction time limit in milliseconds.
+    uint16_t reserved1;         //!< Reserved.
+    uint16_t samplingPeriod;    //!< Statistics sampling period in seconds.
+} __attribute__((packed));
+
 } // namespace dcmi
 
 #endif