Implement parsing of SBE response data

Changes include:
- Utility interface for parsing the response from the SBE.

Change-Id: Ib05d40a7b9257f0cd53597e1fdee5677994072b1
Signed-off-by: Murulidhar Nataraju <murulidhar@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 554c8ad..cfaa17b 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,10 +1,14 @@
 include_HEADERS = \
- sbe_interfaces.hpp
+    sbe_interfaces.hpp
+
+noinst_HEADERS = \
+    sbe_chipOp_handler.hpp
 
 libsbeinterfacedir = ${libdir}/sbe-interface
 libsbeinterface_LTLIBRARIES = libsbeinterface.la
 
 libsbeinterface_la_SOURCES = \
- sbe_interfaces.cpp
+    sbe_interfaces.cpp \
+    sbe_chipOp_handler.cpp
 
 libsbeinterface_la_LDFLAGS = -version-info 0:0:0 -shared
diff --git a/sbe_chipOp_handler.cpp b/sbe_chipOp_handler.cpp
new file mode 100644
index 0000000..f662690
--- /dev/null
+++ b/sbe_chipOp_handler.cpp
@@ -0,0 +1,116 @@
+#include <stdexcept>
+#include <array>
+#include <sbe_chipOp_handler.hpp>
+namespace openpower
+{
+namespace sbe
+{
+namespace internal
+{
+/*! \union sbeRespHeader_t
+ *  Defines the breakup of the SBE FIFO chip operation response header.
+ */
+union sbeRespHeader_t
+{
+    uint32_t commandWord; /**<Entire 32 bit command word */
+    struct
+    {
+        uint8_t command: 8; /**<command value for which response is obtained */
+        uint8_t commandClass: 8; /**< Class of the command */
+        uint16_t magic: 16; /**< Magic code obtained in the response */
+    };
+} __attribute__((packed));
+
+
+constexpr uint16_t MAGIC_CODE = 0xC0DE;
+constexpr auto SBE_OPERATION_SUCCESSFUL = 0;
+constexpr auto LENGTH_OF_DISTANCE_HEADER_IN_WORDS = 0x1;
+constexpr auto LENGTH_OF_RESP_HEADER_IN_WORDS = 0x2;
+constexpr auto SBEI_SBE_RESPONSE_SIZE_IN_WORDS = ((sizeof(sbeRespHeader_t) + \
+        sizeof(sbe_word_t)) / 4);
+constexpr auto DISTANCE_TO_RESP_CODE = 0x1;
+
+std::vector<sbe_word_t> write(const char* devPath,
+                              const sbe_word_t* cmdBuffer,
+                              size_t cmdBufLen,
+                              size_t respBufLen)
+{
+    //TODO: Add support for reading and writing from the FIFO device
+    std::vector<sbe_word_t> response(respBufLen);
+    return response;
+}
+
+std::vector<sbe_word_t> parseResponse(
+    const std::vector<sbe_word_t>& sbeDataBuf)
+{
+
+    //Number of 32-bit words obtained from the SBE
+    size_t lengthObtained = sbeDataBuf.size();
+
+    //Fetch the SBE header and SBE chiop primary and secondary status
+    //Last value in the buffer will have the offset for the SBE header
+    size_t distanceToStatusHeader =  sbeDataBuf[sbeDataBuf.size() - 1];
+
+    if (lengthObtained < distanceToStatusHeader)
+    {
+        //TODO:use elog infrastructure
+        std::ostringstream errMsg;
+        errMsg << "Distance to SBE status header value " <<
+               distanceToStatusHeader << " is greater then total lenght of "
+               "response buffer " << lengthObtained;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+
+    //Fetch the response header contents
+    sbeRespHeader_t l_respHeader{};
+    auto iter = sbeDataBuf.begin();
+    std::advance(iter, (lengthObtained - distanceToStatusHeader));
+    l_respHeader.commandWord = *iter;
+
+    //Fetch the primary and secondary response code
+    std::advance(iter, DISTANCE_TO_RESP_CODE);
+    auto l_priSecResp = *iter;
+
+    //Validate the magic code obtained in the response
+    if (l_respHeader.magic != MAGIC_CODE)
+    {
+        //TODO:use elog infrastructure
+        std::ostringstream errMsg;
+        errMsg << "Invalid MAGIC keyword in the response header (" <<
+               l_respHeader.magic << "),expected keyword " << MAGIC_CODE;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+
+    //Validate the Primary and Secondary response value
+    if (l_priSecResp != SBE_OPERATION_SUCCESSFUL)
+    {
+        //Extract the SBE FFDC and throw it to the caller
+        size_t ffdcLen = (distanceToStatusHeader -
+                          SBEI_SBE_RESPONSE_SIZE_IN_WORDS -
+                          LENGTH_OF_DISTANCE_HEADER_IN_WORDS);
+        if (ffdcLen)
+        {
+            std::vector<sbe_word_t> ffdcData(ffdcLen);
+            //Fetch the offset of FFDC data
+            auto ffdcOffset = (lengthObtained - distanceToStatusHeader) +
+                              LENGTH_OF_RESP_HEADER_IN_WORDS;
+            std::copy_n((sbeDataBuf.begin() + ffdcOffset), ffdcLen,
+                        ffdcData.begin());
+        }
+
+        //TODO:use elog infrastructure to return the SBE and Hardware procedure
+        //FFDC container back to the caller.
+        std::ostringstream errMsg;
+        errMsg << "Chip operation failed with SBE response code:" << l_priSecResp
+               << ".Length of FFDC data of obtained:" << ffdcLen;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+    return sbeDataBuf;
+
+}
+
+}
+}
+}
+
+
diff --git a/sbe_chipOp_handler.hpp b/sbe_chipOp_handler.hpp
new file mode 100644
index 0000000..565c6fd
--- /dev/null
+++ b/sbe_chipOp_handler.hpp
@@ -0,0 +1,100 @@
+#pragma once
+
+#include <stdexcept>
+#include <array>
+#include <sstream>
+#include <algorithm>
+#include <vector>
+
+namespace openpower
+{
+namespace sbe
+{
+
+using sbe_word_t = uint32_t;
+
+namespace internal
+{
+
+/**
+ * @brief Helper function for invokeSBEChipOperation(),to write to the SBE FIFO
+ * device and obtain the expected response .Internal device driver failures
+ * will be conveyed via respective exceptions.
+ *
+ * Exceptions thrown for:
+ * - Device driver internal failures
+ *
+ * @param[in] FIFO device path associated with SBE.
+ * @param[in] Command buffer to be written to the SBE FIFO
+ * @param[in] Length of command buffer
+ * @param[in] Expected response buffer length
+ *
+ * @return Response buffer returned by the SBE for the input command.
+ */
+std::vector<sbe_word_t> write(const char* devPath,
+                              const sbe_word_t* cmdBuffer,
+                              size_t cmdBufLen,
+                              size_t respBufLen);
+
+/**
+ * @brief Helper function for invokeSBEChipOperation(), to parse the data
+ * obtained from the SBE. The header and SBE response will be verified and on
+ * success the required data will be returned to the caller. SBE interface
+ * failure will be conveyed via respective exceptions.
+ *
+ * Exceptions thrown for:
+ * - SBE Internal failures
+ *
+ * @param[in] SBE data obtained from the SBE FIFO device
+ * @return Valid chip operation response obtained by SBE.
+ */
+std::vector<sbe_word_t> parseResponse(
+    const std::vector<sbe_word_t>& sbeDataBuf);
+
+}//end of internal namespace
+
+/**
+ * @brief Interface to invoke a SBE chip operation.It calls internal API to
+ * write to the SBE FIFO and validates the data obtained by the SBE. It throws
+ * exception for any SBE internal failures.
+ *
+ * Runtime exceptions thrown for:
+ * - Device driver failures
+ * - SBE internal failures
+ *
+ * @param[in] FIFO device path associated with the SBE.
+ * @param[in] Request packet for the data to be read.
+ * @param[in] Data obtained by the SBE.
+ * @tparam S1 Length of request buffer to be send to SBE
+ * @tparam S2 Expected length of data from the SBE
+ */
+template<size_t S1, size_t S2>
+inline void invokeSBEChipOperation(const char* devPath,
+                                   const std::array<sbe_word_t, S1>& request,
+                                   std::array<sbe_word_t, S2>& chipOpData)
+{
+    //Write and read from the FIFO device.
+    auto sbeFifoResp = internal::write(devPath, request.data(), request.size(),
+                                       chipOpData.size());
+
+    //Parse the obtained data
+    auto response = internal::parseResponse(sbeFifoResp);
+
+    if (response.size() != chipOpData.size())
+    {
+        //TODO:use elog infrastructure
+        std::ostringstream errMsg;
+        errMsg << "Obtained chip operation response length (" << response.size()
+               << "from SBE is not equal to the expected length of data (" <<
+               chipOpData.size();
+
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+
+    //Move the contents of response buffer into the output buffer.
+    std::move(response.begin(), response.end(), chipOpData.begin());
+}
+
+}
+}
+
diff --git a/sbe_interfaces.cpp b/sbe_interfaces.cpp
index 8e63b53..28f0bb9 100755
--- a/sbe_interfaces.cpp
+++ b/sbe_interfaces.cpp
@@ -2,12 +2,15 @@
 #include <stdexcept>
 #include <array>
 #include "sbe_interfaces.hpp"
+#include "sbe_chipOp_handler.hpp"
 
 namespace openpower
 {
 namespace sbe
 {
 
+constexpr size_t RESP_HEADER_LEN = 0x3;
+
 //Helper interfaces
 static inline uint32_t upper(uint64_t value)
 {
@@ -29,6 +32,7 @@
 static constexpr sbe_word_t WRITE_OPCODE = 0x0000A202;
 static constexpr size_t READ_CMD_LENGTH = 0x4;
 static constexpr size_t WRITE_CMD_LENGTH = 0x6;
+static constexpr size_t READ_RESP_LENGTH = 0x2;
 
 //Reading SCOM Registers
 uint64_t read(const char* devPath,
@@ -51,11 +55,16 @@
         lower(address)
     };
 
-    std::cout << "Size of read command buffer:" << command.size();
+    //Buffer to hold the response data along with the SBE header
+    const size_t respLength = RESP_HEADER_LEN + READ_RESP_LENGTH ;
+    std::array<sbe_word_t, respLength> response = {};
 
-    // TODO: Call an interface to read the command to the SBE FIFO and read the
-    // response from the SBE FIFO device
+    //Write the command buffer to the SBE FIFO and obtain the response from the
+    //SBE FIFO device.This interface will parse the obtained SBE response and
+    //any internal SBE failures will be communicated via exceptions
+    invokeSBEChipOperation(devPath, command, response);
 
+    value = (((static_cast<uint64_t>(response[0])) << 32) | response[1]);
     return value;
 }
 
@@ -80,11 +89,14 @@
         lower(data)
     };
 
-    std::cout << "Size of write command buffer:" << command.size();
- 
-    // TODO: Call an interface to write the command to the SBE FIFO and read the
-    // response from the SBE FIFO device
+    //Buffer to hold the SBE response status
+    const size_t respLength = RESP_HEADER_LEN;
+    std::array<sbe_word_t, respLength> response = {};
 
+    //Write the command buffer to the SBE FIFO and obtain the response from the
+    //SBE FIFO device.This interface will parse the obtained SBE response and
+    //any internal SBE failures will be communicated via exceptions
+    invokeSBEChipOperation(devPath, command, response);
 }
 
 } // namespace scom