dump-collect: Add application for SBE dump collection

This series of commits introduces the `dump-collect` application, a new
command-line tool designed to facilitate the collection of dumps from
the SBE. The tool is capable of asynchronous operation, allowing it
to initiate dump collection from different SBEs at the same time.

Key Highlights:
- The base implementation sets up the application to accept necessary
   parameters for dump collection, including dump type, dump ID,
   destination path for collected data, and the ID of the failing unit.
- An implementation of the dump collection class and the initiation
  method enables the start of the dump collection process
  asynchronously.
- The `dump-collect` application is designed to be invoked from scripts,
  which collect various data in the case of system failures.

Tests:
  Validated parameters passed
  Starting dump collection method

Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
Change-Id: If6f44075d33af20e09a442d7968d235dc6e8ea16
diff --git a/dump/sbe_dump_collector.cpp b/dump/sbe_dump_collector.cpp
new file mode 100644
index 0000000..5f0f83f
--- /dev/null
+++ b/dump/sbe_dump_collector.cpp
@@ -0,0 +1,120 @@
+extern "C"
+{
+#include <libpdbg.h>
+#include <libpdbg_sbe.h>
+}
+
+#include "sbe_consts.hpp"
+#include "sbe_dump_collector.hpp"
+
+#include <libphal.H>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <phosphor-logging/lg2.hpp>
+#include <phosphor-logging/log.hpp>
+
+#include <format>
+#include <stdexcept>
+
+namespace openpower::dump::sbe_chipop
+{
+
+using namespace phosphor::logging;
+using namespace openpower::dump::SBE;
+
+void SbeDumpCollector::collectDump(uint8_t type, uint32_t id,
+                                   uint64_t failingUnit,
+                                   const std::filesystem::path& path)
+{
+    lg2::error("Starting dump collection: type:{TYPE} id:{ID} "
+               "failingUnit:{FAILINGUNIT}, path:{PATH}",
+               "TYPE", type, "ID", id, "FAILINGUNIT", failingUnit, "PATH",
+               path.string());
+
+    initializePdbg();
+
+    std::vector<struct pdbg_target*> targets;
+
+    struct pdbg_target* target = nullptr;
+    pdbg_for_each_class_target("proc", target)
+    {
+        if (pdbg_target_probe(target) != PDBG_TARGET_ENABLED ||
+            !openpower::phal::pdbg::isTgtFunctional(target))
+        {
+            continue;
+        }
+
+        targets.push_back(target);
+    }
+
+    std::vector<uint8_t> clockStates = {SBE_CLOCK_ON, SBE_CLOCK_OFF};
+    for (auto cstate : clockStates)
+    {
+        auto futures = spawnDumpCollectionProcesses(type, id, path, failingUnit,
+                                                    cstate, targets);
+
+        // Wait for all asynchronous tasks to complete
+        for (auto& future : futures)
+        {
+            future.wait();
+        }
+        lg2::info(
+            "Dump collection completed for clock state({CSTATE}): type({TYPE}) "
+            "id({ID}) failingUnit({FAILINGUNIT}), path({PATH})",
+            "CSTATE", cstate, "TYPE", type, "ID", id, "FAILINGUNIT",
+            failingUnit, "PATH", path.string());
+    }
+
+    lg2::info("Dump collection completed");
+}
+
+void SbeDumpCollector::initializePdbg()
+{
+    openpower::phal::pdbg::init();
+}
+
+std::vector<std::future<void>> SbeDumpCollector::spawnDumpCollectionProcesses(
+    uint8_t type, uint32_t id, const std::filesystem::path& path,
+    uint64_t failingUnit, uint8_t cstate,
+    const std::vector<struct pdbg_target*>& targets)
+{
+    std::vector<std::future<void>> futures;
+
+    for (auto target : targets)
+    {
+        if (pdbg_target_probe(target) != PDBG_TARGET_ENABLED ||
+            !openpower::phal::pdbg::isTgtFunctional(target))
+        {
+            continue;
+        }
+
+        // Launch an asynchronous task instead of forking
+        auto future =
+            std::async(std::launch::async,
+                       [this, target, path, id, type, cstate, failingUnit]() {
+            this->collectDumpFromSBE(target, path, id, type, cstate,
+                                     failingUnit);
+        });
+
+        futures.push_back(std::move(future));
+    }
+
+    return futures;
+}
+
+void SbeDumpCollector::collectDumpFromSBE(struct pdbg_target* chip,
+                                          const std::filesystem::path& path,
+                                          uint32_t id, uint8_t type,
+                                          uint8_t clockState,
+                                          uint64_t failingUnit)
+{
+    auto chipPos = pdbg_target_index(chip);
+    lg2::info(
+        "Collecting dump from proc({PROC}): path({PATH}) id({ID}) "
+        "type({TYPE}) clockState({CLOCKSTATE}) failingUnit({FAILINGUNIT})",
+        "PROC", chipPos, "PATH", path.string(), "ID", id, "TYPE", type,
+        "CLOCKSTATE", clockState, "FAILINGUNIT", failingUnit);
+}
+
+} // namespace openpower::dump::sbe_chipop