pldmd: implement PLDM.Requester D-Bus API

Implement D-Bus API defined at
https://gerrit.openbmc-project.xyz/#/c/openbmc/phosphor-dbus-interfaces/+/27077/.

This commit provides an implementation of the PLDM instance id as per
DSP0240 v1.0.0. Ids are tracked per MCTP EID.

Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I8a1ef213646529b71d9bf33edda67537f7c0f32a
diff --git a/dbus_impl_requester.cpp b/dbus_impl_requester.cpp
new file mode 100644
index 0000000..ec0fc53
--- /dev/null
+++ b/dbus_impl_requester.cpp
@@ -0,0 +1,37 @@
+#include "dbus_impl_requester.hpp"
+
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using namespace phosphor::logging;
+
+namespace pldm
+{
+namespace dbus_api
+{
+
+uint8_t Requester::getInstanceId(uint8_t eid)
+{
+    if (ids.find(eid) == ids.end())
+    {
+        InstanceId id;
+        ids.emplace(eid, InstanceId());
+    }
+
+    uint8_t id{};
+    try
+    {
+        id = ids[eid].next();
+    }
+    catch (const std::runtime_error& e)
+    {
+        elog<TooManyResources>();
+    }
+
+    return id;
+}
+
+} // namespace dbus_api
+} // namespace pldm
diff --git a/dbus_impl_requester.hpp b/dbus_impl_requester.hpp
new file mode 100644
index 0000000..e892194
--- /dev/null
+++ b/dbus_impl_requester.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "instance_id.hpp"
+#include "xyz/openbmc_project/PLDM/Requester/server.hpp"
+
+#include <map>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+
+namespace pldm
+{
+namespace dbus_api
+{
+
+using RequesterIntf = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::PLDM::server::Requester>;
+
+/** @class Requester
+ *  @brief OpenBMC PLDM.Requester implementation.
+ *  @details A concrete implementation for the
+ *  xyz.openbmc_project.PLDM.Requester DBus APIs.
+ */
+class Requester : public RequesterIntf
+{
+  public:
+    Requester() = delete;
+    Requester(const Requester&) = delete;
+    Requester& operator=(const Requester&) = delete;
+    Requester(Requester&&) = delete;
+    Requester& operator=(Requester&&) = delete;
+    virtual ~Requester() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] path - Path to attach at.
+     */
+    Requester(sdbusplus::bus::bus& bus, const std::string& path) :
+        RequesterIntf(bus, path.c_str()){};
+
+    /** @brief Implementation for RequesterIntf.GetInstanceId */
+    uint8_t getInstanceId(uint8_t eid) override;
+
+    /** @brief Mark an instance id as unused
+     *  @param[in] eid - MCTP eid to which this instance id belongs
+     *  @param[in] instanceId - PLDM instance id to be freed
+     *  @note will throw std::out_of_range if instanceId > 31
+     */
+    void markFree(uint8_t eid, uint8_t instanceId)
+    {
+        ids[eid].markFree(instanceId);
+    }
+
+  private:
+    /** @brief EID to PLDM Instance ID map */
+    std::map<uint8_t, InstanceId> ids;
+};
+
+} // namespace dbus_api
+} // namespace pldm
diff --git a/instance_id.cpp b/instance_id.cpp
new file mode 100644
index 0000000..52cd1d6
--- /dev/null
+++ b/instance_id.cpp
@@ -0,0 +1,25 @@
+#include "instance_id.hpp"
+
+#include <stdexcept>
+
+namespace pldm
+{
+
+uint8_t InstanceId::next()
+{
+    uint8_t idx = 0;
+    while (idx < id.size() && id.test(idx))
+    {
+        ++idx;
+    }
+
+    if (idx == id.size())
+    {
+        throw std::runtime_error("No free instance ids");
+    }
+
+    id.set(idx);
+    return idx;
+}
+
+} // namespace pldm
diff --git a/instance_id.hpp b/instance_id.hpp
new file mode 100644
index 0000000..70b284c
--- /dev/null
+++ b/instance_id.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <bitset>
+
+namespace pldm
+{
+
+constexpr size_t maxInstanceIds = 32;
+
+/** @class InstanceId
+ *  @brief Implementation of PLDM instance id as per DSP0240 v1.0.0
+ */
+class InstanceId
+{
+  public:
+    /** @brief Get next unused instance id
+     *  @return - PLDM instance id
+     */
+    uint8_t next();
+
+    /** @brief Mark an instance id as unused
+     *  @param[in] instanceId - PLDM instance id to be freed
+     */
+    void markFree(uint8_t instanceId)
+    {
+        id.set(instanceId, false);
+    }
+
+  private:
+    std::bitset<maxInstanceIds> id;
+};
+
+} // namespace pldm
diff --git a/meson.build b/meson.build
index bca95eb..3a1e010 100644
--- a/meson.build
+++ b/meson.build
@@ -37,12 +37,15 @@
   libpldm,
   libpldmresponder,
   dependency('sdbusplus'),
-  dependency('sdeventplus')
+  dependency('sdeventplus'),
+  dependency('phosphor-dbus-interfaces')
 ]
 
 executable(
   'pldmd',
   'pldmd.cpp',
+  'dbus_impl_requester.cpp',
+  'instance_id.cpp',
   implicit_include_directories: false,
   dependencies: deps,
   install: true,
diff --git a/pldmd.cpp b/pldmd.cpp
index dce5625..39bee54 100644
--- a/pldmd.cpp
+++ b/pldmd.cpp
@@ -1,3 +1,4 @@
+#include "dbus_impl_requester.hpp"
 #include "libpldmresponder/base.hpp"
 #include "libpldmresponder/bios.hpp"
 #include "libpldmresponder/platform.hpp"
@@ -40,7 +41,8 @@
 using namespace sdeventplus;
 using namespace sdeventplus::source;
 
-static Response processRxMsg(const std::vector<uint8_t>& requestMsg)
+static Response processRxMsg(const std::vector<uint8_t>& requestMsg,
+                             dbus_api::Requester& requester)
 {
 
     Response response;
@@ -78,6 +80,10 @@
             response.insert(response.end(), completion_code);
         }
     }
+    else
+    {
+        requester.markFree(eid, hdr->instance_id);
+    }
     return response;
 }
 
@@ -180,7 +186,10 @@
         exit(EXIT_FAILURE);
     }
 
-    auto callback = [verbose](IO& /*io*/, int fd, uint32_t revents) {
+    auto bus = sdbusplus::bus::new_default();
+    dbus_api::Requester dbusImplReq(bus, "/xyz/openbmc_project/pldm");
+    auto callback = [verbose, &dbusImplReq](IO& /*io*/, int fd,
+                                            uint32_t revents) {
         if (!(revents & EPOLLIN))
         {
             return;
@@ -231,7 +240,7 @@
                 else
                 {
                     // process message and send response
-                    auto response = processRxMsg(requestMsg);
+                    auto response = processRxMsg(requestMsg, dbusImplReq);
                     if (!response.empty())
                     {
                         if (verbose)
@@ -269,6 +278,8 @@
     };
 
     auto event = Event::get_default();
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    bus.request_name("xyz.openbmc_project.PLDM");
     IO io(event, socketFd(), EPOLLIN, std::move(callback));
     event.loop();
 
diff --git a/test/meson.build b/test/meson.build
index 1749605..ee509db 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -14,6 +14,7 @@
 
 gtest = dependency('gtest', main: true, disabler: true, required: true)
 gmock = dependency('gmock', disabler: true, required: true)
+pldmd = declare_dependency(sources: '../instance_id.cpp')
 
 tests = [
   'libpldm_base_test',
@@ -26,7 +27,8 @@
   'libpldmresponder_bios_table_test',
   'libpldmresponder_platform_test',
   'libpldm_fru_test',
-  'libpldm_utils_test'
+  'libpldm_utils_test',
+  'pldmd_instanceid_test',
 ]
 
 if get_option('oem-ibm').enabled()
@@ -41,6 +43,14 @@
                      implicit_include_directories: false,
                      link_args: dynamic_linker,
                      build_rpath: get_option('oe-sdk').enabled() ? rpath : '',
-                     dependencies: [libpldm, libpldmresponder, gtest, gmock, dependency('sdbusplus'),dependency('phosphor-logging')]),
+                     dependencies: [
+                         libpldm,
+                         libpldmresponder,
+                         gtest,
+                         gmock,
+                         pldmd,
+                         dependency('phosphor-dbus-interfaces'),
+                         dependency('phosphor-logging'),
+                         dependency('sdbusplus')]),
        workdir: meson.current_source_dir())
 endforeach
diff --git a/test/pldmd_instanceid_test.cpp b/test/pldmd_instanceid_test.cpp
new file mode 100644
index 0000000..35ff146
--- /dev/null
+++ b/test/pldmd_instanceid_test.cpp
@@ -0,0 +1,39 @@
+#include "instance_id.hpp"
+
+#include <stdexcept>
+
+#include <gtest/gtest.h>
+
+using namespace pldm;
+
+TEST(InstanceId, testNext)
+{
+    InstanceId id;
+    ASSERT_EQ(id.next(), 0);
+    ASSERT_EQ(id.next(), 1);
+}
+
+TEST(InstanceId, testAllUsed)
+{
+    InstanceId id;
+    for (size_t i = 0; i < maxInstanceIds; ++i)
+    {
+        ASSERT_EQ(id.next(), i);
+    }
+    EXPECT_THROW(id.next(), std::runtime_error);
+}
+
+TEST(InstanceId, testMarkfree)
+{
+    InstanceId id;
+    for (size_t i = 0; i < maxInstanceIds; ++i)
+    {
+        ASSERT_EQ(id.next(), i);
+    }
+    id.markFree(5);
+    ASSERT_EQ(id.next(), 5);
+    id.markFree(0);
+    ASSERT_EQ(id.next(), 0);
+    EXPECT_THROW(id.next(), std::runtime_error);
+    EXPECT_THROW(id.markFree(32), std::out_of_range);
+}