Add Get SEL entry command

Change-Id: I09983a127c7c2817a0ebb6d64ffb2a9585dcd235
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/selutility.hpp b/selutility.hpp
index 9c253bd..ddf003a 100644
--- a/selutility.hpp
+++ b/selutility.hpp
@@ -41,6 +41,23 @@
     uint8_t operationSupport;       //!< Operation support.
 } __attribute__((packed));
 
+static constexpr auto firstEntry = 0x0000;
+static constexpr auto lastEntry = 0xFFFF;
+static constexpr auto entireRecord = 0xFF;
+static constexpr auto selRecordSize = 16;
+
+/** @struct GetSELEntryRequest
+ *
+ *  IPMI payload for Get SEL Entry command request.
+ */
+struct GetSELEntryRequest
+{
+    uint16_t reservationID;         //!< Reservation ID.
+    uint16_t selRecordID;           //!< SEL Record ID.
+    uint8_t offset;                 //!< Offset into record.
+    uint8_t readLength;             //!< Bytes to read.
+} __attribute__((packed));
+
 /** @struct GetSELEntryResponse
  *
  *  IPMI payload for Get SEL Entry command response.
diff --git a/storagehandler.cpp b/storagehandler.cpp
index bbcb441..66db77b 100644
--- a/storagehandler.cpp
+++ b/storagehandler.cpp
@@ -8,6 +8,7 @@
 #include "storagehandler.h"
 #include "storageaddsel.h"
 #include "host-ipmid/ipmid-api.h"
+#include <experimental/filesystem>
 #include <phosphor-logging/log.hpp>
 #include <sdbusplus/server.hpp>
 #include "xyz/openbmc_project/Common/error.hpp"
@@ -94,6 +95,120 @@
     return IPMI_CC_OK;
 }
 
+ipmi_ret_t getSELEntry(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 ipmi::sel::GetSELEntryRequest*>
+                   (request);
+
+    if (requestData->reservationID != 0)
+    {
+        if (g_sel_reserve != requestData->reservationID)
+        {
+            *data_len = 0;
+            return IPMI_CC_INVALID_RESERVATION_ID;
+        }
+    }
+
+    if (cache::paths.empty())
+    {
+        *data_len = 0;
+        return IPMI_CC_SENSOR_INVALID;
+    }
+
+    ipmi::sel::ObjectPaths::const_iterator iter;
+
+    // Check for the requested SEL Entry.
+    if (requestData->selRecordID == ipmi::sel::firstEntry)
+    {
+        iter = cache::paths.begin();
+    }
+    else if (requestData->selRecordID == ipmi::sel::lastEntry)
+    {
+        iter = cache::paths.end();
+    }
+    else
+    {
+        std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
+                              std::to_string(requestData->selRecordID);
+
+        iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
+        if (iter == cache::paths.end())
+        {
+            *data_len = 0;
+            return IPMI_CC_SENSOR_INVALID;
+        }
+    }
+
+    ipmi::sel::GetSELEntryResponse record {};
+
+    // Convert the log entry into SEL record.
+    try
+    {
+        record = ipmi::sel::convertLogEntrytoSEL(*iter);
+    }
+    catch (InternalFailure& e)
+    {
+        *data_len = 0;
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+    catch (const std::runtime_error& e)
+    {
+        log<level::ERR>(e.what());
+        *data_len = 0;
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+
+    // Identify the next SEL record ID
+    if(iter != cache::paths.end())
+    {
+        ++iter;
+        if (iter == cache::paths.end())
+        {
+            record.nextRecordID = ipmi::sel::lastEntry;
+        }
+        else
+        {
+            namespace fs = std::experimental::filesystem;
+            fs::path path(*iter);
+            record.nextRecordID = static_cast<uint16_t>
+                     (std::stoul(std::string(path.filename().c_str())));
+        }
+    }
+    else
+    {
+        record.nextRecordID = ipmi::sel::lastEntry;
+    }
+
+    if (requestData->readLength == ipmi::sel::entireRecord)
+    {
+        memcpy(response, &record, sizeof(record));
+        *data_len = sizeof(record);
+    }
+    else
+    {
+        if (requestData->offset >= ipmi::sel::selRecordSize ||
+            requestData->readLength > ipmi::sel::selRecordSize)
+        {
+            *data_len = 0;
+            return IPMI_CC_INVALID_FIELD_REQUEST;
+        }
+
+        auto diff = ipmi::sel::selRecordSize - requestData->offset;
+        auto readLength = std::min(diff,
+                                   static_cast<int>(requestData->readLength));
+
+        memcpy(response, &record.nextRecordID, sizeof(record.nextRecordID));
+        memcpy(static_cast<uint8_t*>(response) + sizeof(record.nextRecordID),
+               &record.recordID + requestData->offset, readLength);
+        *data_len = sizeof(record.nextRecordID) + readLength;
+    }
+
+    return IPMI_CC_OK;
+}
+
 ipmi_ret_t ipmi_storage_get_sel_time(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)
@@ -300,6 +415,11 @@
     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL, NULL, ipmi_storage_reserve_sel,
                            PRIVILEGE_USER);
 
+    // <Get SEL Entry>
+    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY);
+    ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL, getSELEntry,
+                           PRIVILEGE_USER);
+
     // <Add SEL Entry>
     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_ADD_SEL);
     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL, ipmi_storage_add_sel,
diff --git a/storagehandler.h b/storagehandler.h
index 174da06..2bfc172 100644
--- a/storagehandler.h
+++ b/storagehandler.h
@@ -7,6 +7,7 @@
     // Get capability bits
     IPMI_CMD_GET_SEL_INFO   = 0x40,
     IPMI_CMD_RESERVE_SEL    = 0x42,
+    IPMI_CMD_GET_SEL_ENTRY  = 0x43,
     IPMI_CMD_ADD_SEL        = 0x44,
     IPMI_CMD_GET_SEL_TIME   = 0x48,
     IPMI_CMD_SET_SEL_TIME   = 0x49,