libpldmresponder: implement handler for GetPDR

This commit implements the GetPDR command as defined in DSP0248 v1.1.1.
Multipart PDR transfers are still not implemented.

Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: Ic520a914e8228b723521151f4ba1eed947179047
diff --git a/configure.ac b/configure.ac
index ea36dd3..9714f45 100755
--- a/configure.ac
+++ b/configure.ac
@@ -82,6 +82,7 @@
 
 AC_DEFINE(BIOS_JSONS_DIR, "/etc/default/obmc/pldm/bios", [Directory housing the json files for BIOS tables])
 AC_DEFINE(BIOS_TABLES_DIR, "/var/lib/pldm/bios", [Directory housing actual BIOS tables])
+AC_DEFINE(PDR_JSONS_DIR, "/var/lib/pldm/pdr", [Directory housing platform specific PDR config])
 
 # Create configured output
 AC_CONFIG_FILES([Makefile libpldm/Makefile libpldmresponder/Makefile test/Makefile tool/Makefile])
diff --git a/libpldm/platform.h b/libpldm/platform.h
index 746ae4f..8e186a0 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -43,6 +43,12 @@
 	PLDM_DISABLE_EFECTER
 };
 
+/** @brief PLDM Platform M&C completion codes
+ */
+enum pldm_platform_completion_codes {
+	PLDM_PLATFORM_INVALID_RECORD_HANDLE = 0x82,
+};
+
 /** @struct pldm_pdr_hdr
  *
  *  Structure representing PLDM common PDR header
diff --git a/libpldmresponder/Makefile.am b/libpldmresponder/Makefile.am
index 948898f..a73252c 100644
--- a/libpldmresponder/Makefile.am
+++ b/libpldmresponder/Makefile.am
@@ -7,8 +7,8 @@
 	effecters.cpp \
 	pdr.cpp \
 	bios_parser.cpp \
-	bios.cpp
-
+	bios.cpp \
+	platform.cpp
 libpldmresponder_la_LIBADD = \
 	../libpldm/libpldm.la \
 	$(CODE_COVERAGE_LIBS)
diff --git a/libpldmresponder/meson.build b/libpldmresponder/meson.build
index c63685c..45dff73 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -13,6 +13,7 @@
   'pdr.cpp',
   'effecters.cpp',
   'utils.cpp',
+  'platform.cpp',
   '../registration.cpp'
 ]
 
diff --git a/libpldmresponder/platform.cpp b/libpldmresponder/platform.cpp
new file mode 100644
index 0000000..5fa14c8
--- /dev/null
+++ b/libpldmresponder/platform.cpp
@@ -0,0 +1,87 @@
+#include "config.h"
+
+#include "platform.hpp"
+
+#include "pdr.hpp"
+
+#include <exception>
+#include <phosphor-logging/log.hpp>
+
+namespace pldm
+{
+
+namespace responder
+{
+
+using namespace phosphor::logging;
+
+Response getPDR(const pldm_msg* request, size_t payloadLength)
+{
+    Response response(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_MIN_RESP_BYTES, 0);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    if (payloadLength != PLDM_GET_PDR_REQ_BYTES)
+    {
+        encode_get_pdr_resp(request->hdr.instance_id, PLDM_ERROR_INVALID_LENGTH,
+                            0, 0, 0, 0, nullptr, 0, responsePtr);
+        return response;
+    }
+
+    uint32_t recordHandle{};
+    uint32_t dataTransferHandle{};
+    uint8_t transferOpFlag{};
+    uint16_t reqSizeBytes{};
+    uint16_t recordChangeNum{};
+
+    decode_get_pdr_req(request, payloadLength, &recordHandle,
+                       &dataTransferHandle, &transferOpFlag, &reqSizeBytes,
+                       &recordChangeNum);
+
+    uint32_t nextRecordHandle{};
+    uint16_t respSizeBytes{};
+    uint8_t* recordData = nullptr;
+    try
+    {
+        pdr::Repo& pdrRepo = pdr::get(PDR_JSONS_DIR);
+        nextRecordHandle = pdrRepo.getNextRecordHandle(recordHandle);
+        pdr::Entry e;
+        if (reqSizeBytes)
+        {
+            e = pdrRepo.at(recordHandle);
+            respSizeBytes = e.size();
+            if (respSizeBytes > reqSizeBytes)
+            {
+                respSizeBytes = reqSizeBytes;
+            }
+            recordData = e.data();
+        }
+        response.resize(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_MIN_RESP_BYTES +
+                            respSizeBytes,
+                        0);
+        responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+        encode_get_pdr_resp(request->hdr.instance_id, PLDM_SUCCESS,
+                            nextRecordHandle, 0, PLDM_START, respSizeBytes,
+                            recordData, 0, responsePtr);
+    }
+    catch (const std::out_of_range& e)
+    {
+        encode_get_pdr_resp(request->hdr.instance_id,
+                            PLDM_PLATFORM_INVALID_RECORD_HANDLE,
+                            nextRecordHandle, 0, PLDM_START, respSizeBytes,
+                            recordData, 0, responsePtr);
+        return response;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>("Error accessing PDR", entry("HANDLE=%d", recordHandle),
+                        entry("ERROR=%s", e.what()));
+        encode_get_pdr_resp(request->hdr.instance_id, PLDM_ERROR,
+                            nextRecordHandle, 0, PLDM_START, respSizeBytes,
+                            recordData, 0, responsePtr);
+        return response;
+    }
+    return response;
+}
+
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
new file mode 100644
index 0000000..253d786
--- /dev/null
+++ b/libpldmresponder/platform.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "libpldm/platform.h"
+
+namespace pldm
+{
+
+using Response = std::vector<uint8_t>;
+
+namespace responder
+{
+
+/** @brief Handler for GetPDR
+ *
+ *  @param[in] request - Request message payload
+ *  @param[in] payloadLength - Request payload length
+ *  @param[out] Response - Response message written here
+ */
+Response getPDR(const pldm_msg* request, size_t payloadLength);
+
+} // namespace responder
+} // namespace pldm
diff --git a/meson.build b/meson.build
index b71e83c..1a0b5de 100644
--- a/meson.build
+++ b/meson.build
@@ -16,6 +16,7 @@
 conf_data = configuration_data()
 conf_data.set_quoted('BIOS_JSONS_DIR', '/etc/default/obmc/pldm/bios')
 conf_data.set_quoted('BIOS_TABLES_DIR', '/var/lib/pldm/bios')
+conf_data.set_quoted('PDR_JSONS_DIR', '/etc/default/obmc/pldm/pdr')
 if get_option('oem-ibm').enabled()
   conf_data.set_quoted('FILE_TABLE_JSON', '/etc/default/obmc/pldm/fileTable.json')
   add_global_arguments('-DOEM_IBM', language : 'c')
diff --git a/test/Makefile.am b/test/Makefile.am
index 4b0acca..fbaa01f 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -9,7 +9,8 @@
 	libpldm_bios_test \
 	libpldmresponder_bios_test \
 	libpldmresponder_pdr_state_effecter_test \
-	libpldmresponder_bios_table_test
+	libpldmresponder_bios_table_test \
+	libpldmresponder_platform_test
 
 if OEM_IBM
 check_PROGRAMS += \
@@ -158,3 +159,19 @@
 	-lstdc++fs
 libpldmresponder_bios_table_test_SOURCES = \
         libpldmresponder_bios_table_test.cpp
+
+libpldmresponder_platform_test_CPPFLAGS = $(test_cppflags)
+libpldmresponder_platform_test_CXXFLAGS = $(test_cxxflags)
+libpldmresponder_platform_test_LDFLAGS = $(test_ldflags)
+libpldmresponder_platform_test_LDADD = \
+	$(top_builddir)/libpldm/libpldm_la-base.o \
+	$(top_builddir)/libpldm/libpldm_la-platform.o \
+	$(top_builddir)/libpldmresponder/libpldmresponder_la-pdr.o \
+	$(top_builddir)/libpldmresponder/libpldmresponder_la-effecters.o \
+	$(top_builddir)/libpldmresponder/libpldmresponder_la-platform.o \
+	$(PHOSPHOR_LOGGING_LIBS) \
+	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+	$(SDBUSPLUS_LIBS) \
+	$(CODE_COVERAGE_LIBS) \
+	-lstdc++fs
+libpldmresponder_platform_test_SOURCES = libpldmresponder_platform_test.cpp
diff --git a/test/libpldmresponder_pdr_state_effecter_test.cpp b/test/libpldmresponder_pdr_state_effecter_test.cpp
index 24eea41..f763593 100644
--- a/test/libpldmresponder_pdr_state_effecter_test.cpp
+++ b/test/libpldmresponder_pdr_state_effecter_test.cpp
@@ -11,7 +11,7 @@
 {
     using namespace pdr;
     using namespace effecter::dbus_mapping;
-    Repo& pdrRepo = get("./pdr_jsons/good");
+    Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
 
     // 2 entries
     ASSERT_EQ(pdrRepo.numEntries(), 2);
@@ -100,8 +100,8 @@
 TEST(GeneratePDR, testMalformedJson)
 {
     using namespace pdr;
-    Repo& pdrRepo = get("./pdr_jsons/good");
+    Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
     ASSERT_EQ(pdrRepo.numEntries(), 2);
     pdrRepo.makeEmpty();
-    ASSERT_THROW(get("./pdr_jsons/malformed"), std::exception);
+    ASSERT_THROW(get("./pdr_jsons/state_effecter/malformed"), std::exception);
 }
diff --git a/test/libpldmresponder_platform_test.cpp b/test/libpldmresponder_platform_test.cpp
new file mode 100644
index 0000000..dd51e72
--- /dev/null
+++ b/test/libpldmresponder_platform_test.cpp
@@ -0,0 +1,173 @@
+#include "libpldmresponder/pdr.hpp"
+#include "libpldmresponder/platform.hpp"
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+using namespace pldm::responder;
+
+TEST(getPDR, testGoodPath)
+{
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES>
+        requestPayload{};
+    auto request = reinterpret_cast<pldm_msg*>(requestPayload.data());
+    size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr);
+
+    uint8_t* start = request->payload;
+    start += sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+    uint16_t* reqCount = reinterpret_cast<uint16_t*>(start);
+    *reqCount = 100;
+    using namespace pdr;
+    Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
+    ASSERT_EQ(pdrRepo.empty(), false);
+    auto response = getPDR(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
+    start = responsePtr->payload;
+    start += sizeof(uint8_t);
+    uint32_t* nextRecordHandle = reinterpret_cast<uint32_t*>(start);
+    ASSERT_EQ(*nextRecordHandle, 2);
+    start += sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+    uint16_t* recordCount = reinterpret_cast<uint16_t*>(start);
+    ASSERT_EQ(*recordCount != 0, true);
+    start += sizeof(uint16_t);
+    // Check a bit of the PDR common header
+    pldm_state_effecter_pdr* pdr =
+        reinterpret_cast<pldm_state_effecter_pdr*>(start);
+    ASSERT_EQ(pdr->hdr.record_handle, 1);
+    ASSERT_EQ(pdr->hdr.version, 1);
+}
+
+TEST(getPDR, testShortRead)
+{
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES>
+        requestPayload{};
+    auto request = reinterpret_cast<pldm_msg*>(requestPayload.data());
+    size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr);
+
+    uint8_t* start = request->payload;
+    start += sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+    uint16_t* reqCount = reinterpret_cast<uint16_t*>(start);
+    // Read 1 byte of PDR
+    *reqCount = 1;
+    using namespace pdr;
+    Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
+    ASSERT_EQ(pdrRepo.empty(), false);
+    auto response = getPDR(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
+    start = responsePtr->payload;
+    start +=
+        sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+    uint16_t* recordCount = reinterpret_cast<uint16_t*>(start);
+    ASSERT_EQ(*recordCount, 1);
+}
+
+TEST(getPDR, testBadRecordHandle)
+{
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES>
+        requestPayload{};
+    auto request = reinterpret_cast<pldm_msg*>(requestPayload.data());
+    size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr);
+
+    uint8_t* start = request->payload;
+    uint32_t* recordHandle = reinterpret_cast<uint32_t*>(start);
+    *recordHandle = 100000;
+    start += sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+    uint16_t* reqCount = reinterpret_cast<uint16_t*>(start);
+    *reqCount = 1;
+    using namespace pdr;
+    Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
+    ASSERT_EQ(pdrRepo.empty(), false);
+    auto response = getPDR(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    ASSERT_EQ(responsePtr->payload[0], PLDM_PLATFORM_INVALID_RECORD_HANDLE);
+}
+
+TEST(getPDR, testNoNextRecord)
+{
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES>
+        requestPayload{};
+    auto request = reinterpret_cast<pldm_msg*>(requestPayload.data());
+    size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr);
+
+    uint8_t* start = request->payload;
+    uint32_t* recordHandle = reinterpret_cast<uint32_t*>(start);
+    *recordHandle = 3;
+    using namespace pdr;
+    Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
+    ASSERT_EQ(pdrRepo.empty(), false);
+    auto response = getPDR(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
+    start = responsePtr->payload;
+    start += sizeof(uint8_t);
+    uint32_t* nextRecordHandle = reinterpret_cast<uint32_t*>(start);
+    ASSERT_EQ(*nextRecordHandle, 0);
+}
+
+TEST(getPDR, testFindPDR)
+{
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES>
+        requestPayload{};
+    auto request = reinterpret_cast<pldm_msg*>(requestPayload.data());
+    size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr);
+
+    uint8_t* start = request->payload;
+    start += sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+    uint16_t* reqCount = reinterpret_cast<uint16_t*>(start);
+    *reqCount = 100;
+    using namespace pdr;
+    Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
+    ASSERT_EQ(pdrRepo.empty(), false);
+    auto response = getPDR(request, requestPayloadLength);
+
+    // Let's try to find a PDR of type stateEffecter (= 11) and entity type =
+    // 100
+    bool found = false;
+    uint32_t handle = 0; // start asking for PDRs from recordHandle 0
+    uint32_t* recordHandle = nullptr;
+    while (!found)
+    {
+        start = request->payload;
+        recordHandle = reinterpret_cast<uint32_t*>(start);
+        *recordHandle = handle;
+        auto response = getPDR(request, requestPayloadLength);
+        auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+        ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
+        start = responsePtr->payload;
+        start += sizeof(uint8_t);
+        uint32_t* nextRecordHandle = reinterpret_cast<uint32_t*>(start);
+        handle = *nextRecordHandle; // point to the next pdr in case current is
+                                    // not what we want
+        start += sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t) +
+                 sizeof(uint16_t);
+        pldm_pdr_hdr* hdr = reinterpret_cast<pldm_pdr_hdr*>(start);
+        uint32_t intType = hdr->type;
+        std::cerr << "PDR next record handle " << handle << std::endl;
+        std::cerr << "PDR type " << intType << std::endl;
+        if (hdr->type == PLDM_STATE_EFFECTER_PDR)
+        {
+            pldm_state_effecter_pdr* pdr =
+                reinterpret_cast<pldm_state_effecter_pdr*>(start);
+            std::cerr << "PDR entity type " << pdr->entity_type << std::endl;
+            if (pdr->entity_type == 100)
+            {
+                found = true;
+                // Rest of the PDR can be accessed as need be
+                break;
+            }
+        }
+        if (!*nextRecordHandle) // no more records
+        {
+            break;
+        }
+    }
+    ASSERT_EQ(found, true);
+}
diff --git a/test/meson.build b/test/meson.build
index 93135fd..1ddb598 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -22,7 +22,8 @@
   'libpldm_bios_test',
   'libpldmresponder_bios_test',
   'libpldmresponder_pdr_state_effecter_test',
-  'libpldmresponder_bios_table_test'
+  'libpldmresponder_bios_table_test',
+  'libpldmresponder_platform_test'
 ]
 
 if get_option('oem-ibm').enabled()
diff --git a/test/pdr_jsons/good/11.json b/test/pdr_jsons/state_effecter/good/11.json
similarity index 100%
rename from test/pdr_jsons/good/11.json
rename to test/pdr_jsons/state_effecter/good/11.json
diff --git a/test/pdr_jsons/malformed/11.json b/test/pdr_jsons/state_effecter/malformed/11.json
similarity index 100%
rename from test/pdr_jsons/malformed/11.json
rename to test/pdr_jsons/state_effecter/malformed/11.json