Refactor SST host processor interface

In order to support future host processors that use a different
interface to SST, separate the SST logic into 1) high-level discovery
logic + D-Bus interfaces, and 2) low-level backend processor interface.

This is a pure refactor with no functional change.

Tested:
Ran sst-compare-redfish-os.py tool on platform with SPR host CPU, and
verified no mismatches reported.
Used sst-info.sh to change configs and verify new config was reflected
in Redfish.

Change-Id: I6825eb7541cbe2214844e7b64d462f2688dedcec
Signed-off-by: Jonathan Doman <jonathan.doman@intel.com>
diff --git a/include/speed_select.hpp b/include/speed_select.hpp
index aaa8b70..4b4189f 100644
--- a/include/speed_select.hpp
+++ b/include/speed_select.hpp
@@ -13,9 +13,14 @@
 // limitations under the License.
 #pragma once
 
+#include <peci.h>
+
 #include <boost/asio/io_context.hpp>
 #include <sdbusplus/asio/connection.hpp>
 
+#include <bitset>
+#include <iostream>
+
 namespace cpu_info
 {
 namespace sst
@@ -36,5 +41,148 @@
 void init(boost::asio::io_context& ioc,
           const std::shared_ptr<sdbusplus::asio::connection>& conn);
 
+class PECIError : public std::runtime_error
+{
+    using std::runtime_error::runtime_error;
+};
+
+bool checkPECIStatus(EPECIStatus libStatus, uint8_t completionCode);
+
+constexpr int extendedModel(CPUModel model)
+{
+    return (model >> 16) & 0xF;
+}
+
+/**
+ * Construct a list of indexes of the set bits in the input value.
+ * E.g. fn(0x7A) -> {1,3,4,5,6}
+ *
+ * @param[in]   mask    Bitmask to convert.
+ *
+ * @return  List of bit indexes.
+ */
+static std::vector<uint32_t> convertMaskToList(std::bitset<64> mask)
+{
+    std::vector<uint32_t> bitList;
+    for (size_t i = 0; i < mask.size(); ++i)
+    {
+        if (mask.test(i))
+        {
+            bitList.push_back(i);
+        }
+    }
+    return bitList;
+}
+
+using TurboEntry = std::tuple<uint32_t, size_t>;
+
+/**
+ * Abstract interface that must be implemented by backends, allowing discovery
+ * and control of a single CPU package.
+ */
+class SSTInterface
+{
+  public:
+    virtual ~SSTInterface()
+    {}
+
+    /**
+     * Whether the interface is ready to be used, or we need to wait longer. The
+     * backend may need to wait e.g. for the host BIOS to initialize the
+     * interface.
+     */
+    virtual bool ready() = 0;
+
+    /** Whether the processor supports the control ("set") functions. */
+    virtual bool supportsControl() = 0;
+
+    /** Whether SST-PP is enabled on the processor. */
+    virtual bool ppEnabled() = 0;
+    /** Return the current SST-PP configuration level */
+    virtual unsigned int currentLevel() = 0;
+    /** Return the maximum valid SST-PP configuration level */
+    virtual unsigned int numLevels() = 0;
+
+    /**
+     * Whether the given level is supported. The level indices may be
+     * discontinuous, so this function should be used before querying deeper
+     * properties of a level.
+     */
+    virtual bool levelSupported(unsigned int level) = 0;
+    /** Whether SST-BF is supported in a given level. */
+    virtual bool bfSupported(unsigned int level) = 0;
+    /** Whether SST-TF is supported in a given level. */
+    virtual bool tfSupported(unsigned int level) = 0;
+    /** Whether SST-BF is enabled in a given level. */
+    virtual bool bfEnabled(unsigned int level) = 0;
+    /** Whether SST-TF is enabled in a given level. */
+    virtual bool tfEnabled(unsigned int level) = 0;
+    /** Return the package Thermal Design Power in Watts for a given level. */
+    virtual unsigned int tdp(unsigned int level) = 0;
+    /** Return the number of cores enabled in a given level. */
+    virtual unsigned int coreCount(unsigned int level) = 0;
+    /** Return the list of enabled logical core indices for a given level. */
+    virtual std::vector<unsigned int> enabledCoreList(unsigned int level) = 0;
+    /**
+     * Return the list of TurboEntrys which define the SSE turbo profile for a
+     * given level.
+     */
+    virtual std::vector<TurboEntry> sseTurboProfile(unsigned int level) = 0;
+    /** Return the base frequency (P1) for a given level. */
+    virtual unsigned int p1Freq(unsigned int level) = 0;
+    /** Return the maximum single-core frequency (P0) for a given level. */
+    virtual unsigned int p0Freq(unsigned int level) = 0;
+    /**
+     * Return the DTS max or external Prochot temperature in degrees Celsius
+     * for a given level.
+     */
+    virtual unsigned int prochotTemp(unsigned int level) = 0;
+    /**
+     * Return the list of logical core indices which have high priority when
+     * SST-BF is enabled for a given level.
+     */
+    virtual std::vector<unsigned int>
+        bfHighPriorityCoreList(unsigned int level) = 0;
+    /** Return the high priority base frequency for a given level. */
+    virtual unsigned int bfHighPriorityFreq(unsigned int level) = 0;
+    /** Return the low priority base frequency for a given level. */
+    virtual unsigned int bfLowPriorityFreq(unsigned int level) = 0;
+
+    /** Enable or disable SST-BF for the current configuration. */
+    virtual void setBfEnabled(bool enable) = 0;
+    /** Enable or disable SST-TF for the current configuration. */
+    virtual void setTfEnabled(bool enable) = 0;
+    /** Change the current configuration to the given level. */
+    virtual void setCurrentLevel(unsigned int level) = 0;
+};
+
+/**
+ * BackendProvider represents a function which may create an SSTInterface given
+ * a CPU PECI address, and the CPU Model information. Usually the CPUModel is
+ * sufficient to determine if the backend is supported.
+ * Backend should return nullptr to indicate it doesn't support a given CPU.
+ * The SST upper layer will call the registered backend provider functions in
+ * arbitrary order until one of them returns a non-null pointer.
+ */
+using BackendProvider =
+    std::function<std::unique_ptr<SSTInterface>(uint8_t, CPUModel)>;
+
+/**
+ * Backends should use 1 instance of the SSTProviderRegistration macro at file
+ * scope to register their provider function. This static struct instance
+ * register the backend before main() is run, and prevents the upper layer from
+ * having to know about which backends exist.
+ */
+#define SSTProviderRegistration(fn)                                            \
+    struct fn##Register                                                        \
+    {                                                                          \
+        fn##Register()                                                         \
+        {                                                                      \
+            std::cerr << "Registering SST Provider " #fn << std::endl;         \
+            registerBackend(fn);                                               \
+        }                                                                      \
+    } fn##Instance;
+void registerBackend(BackendProvider);
+
 } // namespace sst
 } // namespace cpu_info