Add simulated SCOM/CFAM read support for test

Change-Id: I615f1819cd309528735d63f2e6e4b4181c58fd34
Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
diff --git a/test/meson.build b/test/meson.build
index 8a17b52..911b709 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -116,6 +116,7 @@
   files(
     tc + '.cpp',
     '../util/pdbg.cpp',
+    'pdbg-sim-only.cpp',
   ),
   pdbg_test_dtb,
 ]
@@ -166,6 +167,7 @@
     '../analyzer/resolution.cpp',
     '../analyzer/service_data.cpp',
     '../util/pdbg.cpp',
+    'pdbg-sim-only.cpp',
   ),
   pdbg_test_dtb,
 ]
@@ -189,6 +191,7 @@
     '../analyzer/plugins/p10-tod-plugins.cpp',
     '../analyzer/service_data.cpp',
     '../util/pdbg.cpp',
+    'pdbg-sim-only.cpp',
   ),
   pdbg_test_dtb,
 ]
diff --git a/test/pdbg-sim-only.cpp b/test/pdbg-sim-only.cpp
index efeee51..2e81fe5 100644
--- a/test/pdbg-sim-only.cpp
+++ b/test/pdbg-sim-only.cpp
@@ -8,6 +8,7 @@
 
 #include <assert.h>
 
+#include <test/sim-hw-access.hpp>
 #include <util/pdbg.hpp>
 #include <util/trace.hpp>
 
@@ -22,6 +23,8 @@
 namespace pdbg
 {
 
+//------------------------------------------------------------------------------
+
 // This is the simulated version of this function.
 bool queryLpcTimeout(pdbg_target* target)
 {
@@ -32,5 +35,44 @@
     return g_lpcTimeout;
 }
 
+//------------------------------------------------------------------------------
+
+int getScom(pdbg_target* i_target, uint64_t i_addr, uint64_t& o_val)
+{
+    assert(nullptr != i_target);
+    assert(TYPE_PROC == getTrgtType(i_target) ||
+           TYPE_OCMB == getTrgtType(i_target));
+
+    int rc = sim::ScomAccess::getSingleton().get(i_target, i_addr, o_val);
+
+    if (0 != rc)
+    {
+        trace::err("SCOM read failure: target=%s addr=0x%0" PRIx64,
+                   getPath(i_target), i_addr);
+    }
+
+    return rc;
+}
+
+//------------------------------------------------------------------------------
+
+int getCfam(pdbg_target* i_target, uint32_t i_addr, uint32_t& o_val)
+{
+    assert(nullptr != i_target);
+    assert(TYPE_PROC == getTrgtType(i_target));
+
+    int rc = sim::CfamAccess::getSingleton().get(i_target, i_addr, o_val);
+
+    if (0 != rc)
+    {
+        trace::err("CFAM read failure: target=%s addr=0x%08x",
+                   getPath(i_target), i_addr);
+    }
+
+    return rc;
+}
+
+//------------------------------------------------------------------------------
+
 } // namespace pdbg
 } // namespace util
diff --git a/test/sim-hw-access.hpp b/test/sim-hw-access.hpp
new file mode 100644
index 0000000..6bc7a30
--- /dev/null
+++ b/test/sim-hw-access.hpp
@@ -0,0 +1,190 @@
+#include <assert.h>
+
+#include <util/pdbg.hpp>
+
+#include <map>
+
+namespace sim
+{
+
+class ScomAccess
+{
+  private:
+    /** @brief Default constructor. */
+    ScomAccess() = default;
+
+    /** @brief Destructor. */
+    ~ScomAccess() = default;
+
+    /** @brief Copy constructor. */
+    ScomAccess(const ScomAccess&) = delete;
+
+    /** @brief Assignment operator. */
+    ScomAccess& operator=(const ScomAccess&) = delete;
+
+  public:
+    /** @brief Provides access to a singleton instance of this object. */
+    static ScomAccess& getSingleton()
+    {
+        static ScomAccess theScomAccess;
+        return theScomAccess;
+    }
+
+  private:
+    /** The SCOM values for each chip and address. */
+    std::map<pdbg_target*, std::map<uint64_t, uint64_t>> iv_values;
+
+    /** All addresses that will return a SCOM error. */
+    std::map<pdbg_target*, std::map<uint64_t, bool>> iv_errors;
+
+  public:
+    /**
+     * @brief Stores a SCOM register value, which can be accessed later in test.
+     * @param i_target The target chip.
+     * @param i_addr   A SCOM address on the given chip.
+     * @param i_val    The value of the given address.
+     */
+    void add(pdbg_target* i_target, uint64_t i_addr, uint64_t i_val)
+    {
+        assert(nullptr != i_target);
+
+        iv_values[i_target][i_addr] = i_val;
+    }
+
+    /**
+     * @brief This can be used to specify if a specific SCOM address will return
+     *        an error when accessed. This is useful for error path testing.
+     * @param i_target The target chip.
+     * @param i_addr   A SCOM address on the given chip.
+     */
+    void error(pdbg_target* i_target, uint64_t i_addr)
+    {
+        assert(nullptr != i_target);
+
+        iv_errors[i_target][i_addr] = true;
+    }
+
+    /**
+     * @brief Clears all SCOM value/error data.
+     */
+    void flush()
+    {
+        iv_values.clear();
+        iv_errors.clear();
+    }
+
+    /**
+     * @brief  Returns the stored SCOM register value.
+     * @param  i_target The target chip.
+     * @param  i_addr   A SCOM address on the given chip.
+     * @param  o_val    The value of the given address. If the target address
+     *                  does not exist in iv_values, a value of 0 is returned.
+     * @return Will return 1 if the target address exists in iv_errors.
+     *         Otherwise, will return 0 for a successful SCOM access.
+     */
+    int get(pdbg_target* i_target, uint64_t i_addr, uint64_t& o_val)
+    {
+        assert(nullptr != i_target);
+
+        if (iv_errors[i_target][i_addr])
+        {
+            return 1;
+        }
+
+        o_val = iv_values[i_target][i_addr];
+
+        return 0;
+    }
+};
+
+class CfamAccess
+{
+  private:
+    /** @brief Default constructor. */
+    CfamAccess() = default;
+
+    /** @brief Destructor. */
+    ~CfamAccess() = default;
+
+    /** @brief Copy constructor. */
+    CfamAccess(const CfamAccess&) = delete;
+
+    /** @brief Assignment operator. */
+    CfamAccess& operator=(const CfamAccess&) = delete;
+
+  public:
+    /** @brief Provides access to a singleton instance of this object. */
+    static CfamAccess& getSingleton()
+    {
+        static CfamAccess theCfamAccess;
+        return theCfamAccess;
+    }
+
+  private:
+    /** The CFAM values for each chip and address. */
+    std::map<pdbg_target*, std::map<uint32_t, uint32_t>> iv_values;
+
+    /** All addresses that will return a CFAM error. */
+    std::map<pdbg_target*, std::map<uint32_t, bool>> iv_errors;
+
+  public:
+    /**
+     * @brief Stores a CFAM register value, which can be accessed later in test.
+     * @param i_target The target chip.
+     * @param i_addr   A CFAM address on the given chip.
+     * @param i_val    The value of the given address.
+     */
+    void add(pdbg_target* i_target, uint32_t i_addr, uint32_t i_val)
+    {
+        assert(nullptr != i_target);
+
+        iv_values[i_target][i_addr] = i_val;
+    }
+
+    /**
+     * @brief This can be used to specify if a specific CFAM address will return
+     *        an error when accessed. This is useful for error path testing.
+     * @param i_target The target chip.
+     * @param i_addr   A CFAM address on the given chip.
+     */
+    void error(pdbg_target* i_target, uint32_t i_addr)
+    {
+        assert(nullptr != i_target);
+
+        iv_errors[i_target][i_addr] = true;
+    }
+
+    /**
+     * @brief Clears all CFAM value/error data.
+     */
+    void flush()
+    {
+        iv_values.clear();
+        iv_errors.clear();
+    }
+
+    /**
+     * @brief  Returns the stored CFAM register value.
+     * @param  i_target The target chip.
+     * @param  i_addr   A CFAM address on the given chip.
+     * @param  o_val    The value of the given address. If the target address
+     *                  does not exist in iv_values, a value of 0 is returned.
+     * @return Will return 1 if the target address exists in iv_errors.
+     *         Otherwise, will return 0 for a successful CFAM access.
+     */
+    int get(pdbg_target* i_target, uint32_t i_addr, uint32_t& o_val)
+    {
+        assert(nullptr != i_target);
+
+        if (iv_errors[i_target][i_addr])
+        {
+            return 1;
+        }
+
+        o_val = iv_values[i_target][i_addr];
+
+        return 0;
+    }
+};
+
+} // namespace sim
diff --git a/test/test-pdbg-dts.cpp b/test/test-pdbg-dts.cpp
index 1e5075c..9fd49fd 100644
--- a/test/test-pdbg-dts.cpp
+++ b/test/test-pdbg-dts.cpp
@@ -1,5 +1,6 @@
 #include <fcntl.h>
 
+#include <test/sim-hw-access.hpp>
 #include <util/pdbg.hpp>
 #include <util/trace.hpp>
 
@@ -161,3 +162,72 @@
     EXPECT_EQ(memPortUnit,
               getChipUnit(ocmbChip, TYPE_MEM_PORT, memPortUnitPos));
 }
+
+TEST(util_pdbg, getScom)
+{
+    using namespace util::pdbg;
+    pdbg_targets_init(nullptr);
+
+    auto procChip = getTrgt("/proc0");
+    auto ocmbChip = getTrgt("/proc0/pib/perv13/mc1/mi0/mcc0/omi1/ocmb0");
+    auto omiUnit  = getTrgt("/proc0/pib/perv13/mc1/mi0/mcc0/omi1");
+
+    sim::ScomAccess& scom = sim::ScomAccess::getSingleton();
+    scom.flush();
+    scom.add(procChip, 0x11111111, 0x0011223344556677);
+    scom.error(ocmbChip, 0x22222222);
+
+    int rc       = 0;
+    uint64_t val = 0;
+
+    // Test good path.
+    rc = getScom(procChip, 0x11111111, val);
+    EXPECT_EQ(0, rc);
+    EXPECT_EQ(0x0011223344556677, val);
+
+    // Test address that has not been added to ScomAccess.
+    rc = getScom(procChip, 0x33333333, val);
+    EXPECT_EQ(0, rc);
+    EXPECT_EQ(0, val);
+
+    // Test SCOM error.
+    rc = getScom(ocmbChip, 0x22222222, val);
+    EXPECT_EQ(1, rc);
+
+    // Test non-chip target.
+    EXPECT_DEATH({ getScom(omiUnit, 0x11111111, val); }, "");
+}
+
+TEST(util_pdbg, getCfam)
+{
+    using namespace util::pdbg;
+    pdbg_targets_init(nullptr);
+
+    auto procChip = getTrgt("/proc0");
+    auto omiUnit  = getTrgt("/proc0/pib/perv13/mc1/mi0/mcc0/omi1");
+
+    sim::CfamAccess& cfam = sim::CfamAccess::getSingleton();
+    cfam.flush();
+    cfam.add(procChip, 0x11111111, 0x00112233);
+    cfam.error(procChip, 0x22222222);
+
+    int rc       = 0;
+    uint32_t val = 0;
+
+    // Test good path.
+    rc = getCfam(procChip, 0x11111111, val);
+    EXPECT_EQ(0, rc);
+    EXPECT_EQ(0x00112233, val);
+
+    // Test address that has not been added to CfamAccess.
+    rc = getCfam(procChip, 0x33333333, val);
+    EXPECT_EQ(0, rc);
+    EXPECT_EQ(0, val);
+
+    // Test CFAM error.
+    rc = getCfam(procChip, 0x22222222, val);
+    EXPECT_EQ(1, rc);
+
+    // Test non-chip target.
+    EXPECT_DEATH({ getCfam(omiUnit, 0x11111111, val); }, "");
+}