cpuinfoapp: Make PECI features optional

Add a feature flag `cpuinfo-peci` to optionally disable the features in
cpuinfoapp that rely on PECI (PPIN, SST), to support configurations that
want I2C-based SSPEC detection but don't want to use libpeci.

Tested: Disabled `cpuinfo-peci` and verified SSPEC was still written
into the Model property.

Change-Id: Ie3ab9214d9d6ab238a61933de3e3856eca298fa8
Signed-off-by: Jonathan Doman <jonathan.doman@intel.com>
diff --git a/include/cpuinfo.hpp b/include/cpuinfo.hpp
index 200041b..39ae4d1 100644
--- a/include/cpuinfo.hpp
+++ b/include/cpuinfo.hpp
@@ -32,28 +32,28 @@
 static constexpr const int configCheckInterval = 10;
 static constexpr const int peciCheckInterval = 60;
 
-/** \ todo add cpu interface to CPUInfo and consolidate with smbios service
- * using processor =
-    sdbusplus::server::xyz::openbmc_project::inventory::item::Cpu;
-*/
-
-using UniqueIdentifierBase =
+using UniqueIdentifier =
     sdbusplus::server::object_t<sdbusplus::server::xyz::openbmc_project::
                                     inventory::decorator::UniqueIdentifier>;
 
-struct CPUInfo : public UniqueIdentifierBase
+struct CPUInfo
 {
-    CPUInfo(sdbusplus::bus_t& bus, const size_t cpuId,
-            const uint8_t peciAddress, const uint8_t i2cBusNum,
-            const uint8_t i2cSlaveAddress) :
-        // use defer_emit for UniqueIdentifier iface so that ObjectMapper
-        // doesn't find it until we have a valid PPIN
-        UniqueIdentifierBase(bus, (cpuPath + std::to_string(cpuId - 1)).c_str(),
-                             action::defer_emit),
-        id(cpuId), peciAddr(peciAddress), i2cBus(i2cBusNum),
-        i2cDevice(i2cSlaveAddress)
+    CPUInfo(const size_t cpuId, const uint8_t peciAddress,
+            const uint8_t i2cBusNum, const uint8_t i2cSlaveAddress) :
+        id(cpuId),
+        peciAddr(peciAddress), i2cBus(i2cBusNum), i2cDevice(i2cSlaveAddress)
     {}
 
+    void publishUUID(sdbusplus::bus_t& bus, const std::string& uuid)
+    {
+        uuidInterface.emplace(bus, (cpuPath + std::to_string(id - 1)).c_str(),
+                              UniqueIdentifier::action::defer_emit);
+        uuidInterface->uniqueIdentifier(uuid);
+        uuidInterface->emit_added();
+    }
+
+    std::optional<UniqueIdentifier> uuidInterface;
+
     uint8_t id;
     uint8_t peciAddr;
     uint8_t i2cBus;
diff --git a/meson.options b/meson.options
index 1a4cdf8..1c33165 100644
--- a/meson.options
+++ b/meson.options
@@ -34,6 +34,13 @@
 )
 
 option(
+  'cpuinfo-peci',
+  type: 'feature',
+  value: 'enabled',
+  description: 'Enable CPUInfo features that depend on PECI'
+)
+
+option(
   'smbios-ipmi-blob',
   type: 'feature',
   value: 'enabled',
diff --git a/src/cpuinfo_main.cpp b/src/cpuinfo_main.cpp
index 51ae848..78c82b5 100644
--- a/src/cpuinfo_main.cpp
+++ b/src/cpuinfo_main.cpp
@@ -16,7 +16,6 @@
 
 #include "cpuinfo.hpp"
 #include "cpuinfo_utils.hpp"
-#include "speed_select.hpp"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -39,7 +38,11 @@
 #include <linux/i2c-dev.h>
 }
 
+#if PECI_ENABLED
+#include "speed_select.hpp"
+
 #include <peci.h>
+#endif
 
 #include <phosphor-logging/log.hpp>
 #include <sdbusplus/asio/object_server.hpp>
@@ -189,16 +192,20 @@
  * This handles retrying the PIROM reads until two subsequent reads are
  * successful and return matching data. When we have confidence that the data
  * read is correct, then set the property on D-Bus.
- *
- * @param[in,out]   conn        D-Bus connection.
- * @param[in]       cpuInfo     CPU to read from.
  */
 static void
     tryReadSSpec(const std::shared_ptr<sdbusplus::asio::connection>& conn,
-                 const std::shared_ptr<CPUInfo>& cpuInfo)
+                 size_t cpuIndex)
 {
     static int failedReads = 0;
 
+    auto cpuInfoIt = cpuInfoMap.find(cpuIndex);
+    if (cpuInfoIt == cpuInfoMap.end())
+    {
+        return;
+    }
+    auto cpuInfo = cpuInfoIt->second;
+
     std::optional<std::string> newSSpec =
         readSSpec(cpuInfo->i2cBus, cpuInfo->i2cDevice, sspecRegAddr, sspecSize);
     logStream(cpuInfo->id) << "SSpec read status: "
@@ -234,12 +241,12 @@
     auto sspecTimer = std::make_shared<boost::asio::steady_timer>(
         conn->get_io_context(), std::chrono::seconds(retrySeconds));
     sspecTimer->async_wait(
-        [sspecTimer, conn, cpuInfo](boost::system::error_code ec) {
+        [sspecTimer, conn, cpuIndex](boost::system::error_code ec) {
         if (ec)
         {
             return;
         }
-        tryReadSSpec(conn, cpuInfo);
+        tryReadSSpec(conn, cpuIndex);
     });
 }
 
@@ -345,10 +352,10 @@
     }));
 }
 
-static void
-    getProcessorInfo(boost::asio::io_service& io,
-                     const std::shared_ptr<sdbusplus::asio::connection>& conn,
-                     const size_t& cpu)
+#if PECI_ENABLED
+static void getPPIN(boost::asio::io_service& io,
+                    const std::shared_ptr<sdbusplus::asio::connection>& conn,
+                    const size_t& cpu)
 {
     if (cpuInfoMap.find(cpu) == cpuInfoMap.end() || cpuInfoMap[cpu] == nullptr)
     {
@@ -395,7 +402,7 @@
                 }
                 return;
             }
-            getProcessorInfo(io, conn, cpu);
+            getPPIN(io, conn, cpu);
         });
         return;
     }
@@ -453,13 +460,8 @@
                 std::stringstream stream;
                 stream << std::hex << cpuPPIN;
                 std::string serialNumber(stream.str());
-                cpuInfo->uniqueIdentifier(serialNumber);
-                // Signal that the iface is added now so that ObjectMapper and
-                // others can find it.
-                cpuInfo->emit_added();
+                cpuInfo->publishUUID(*conn, serialNumber);
             }
-
-            tryReadSSpec(conn, cpuInfo);
             break;
         }
         default:
@@ -468,6 +470,7 @@
             break;
     }
 }
+#endif
 
 /**
  * Get cpu and pirom address
@@ -485,11 +488,10 @@
                         std::variant<std::string, uint64_t, uint32_t, uint16_t,
                                      std::vector<std::string>>>& properties) {
         const uint64_t* value = nullptr;
-        uint8_t peciAddress = 0;
+        std::optional<uint8_t> peciAddress;
         uint8_t i2cBus = defaultI2cBus;
-        uint8_t i2cDevice;
-        bool i2cDeviceFound = false;
-        size_t cpu = 0;
+        std::optional<uint8_t> i2cDevice;
+        std::optional<size_t> cpu;
 
         if (ec)
         {
@@ -523,7 +525,6 @@
                 if (value != nullptr)
                 {
                     i2cDevice = static_cast<uint8_t>(*value);
-                    i2cDeviceFound = true;
                 }
             }
             if (property.first == "PiromI2cBus")
@@ -536,27 +537,31 @@
             }
         }
 
-        ///\todo replace this with present + power state
-        if (cpu != 0 && peciAddress != 0)
+        if (!cpu || !peciAddress)
         {
-            if (!i2cDeviceFound)
-            {
-                i2cDevice = defaultI2cSlaveAddr0 + cpu - 1;
-            }
-
-            auto key = cpuInfoMap.find(cpu);
-
-            if (key != cpuInfoMap.end())
-            {
-                cpuInfoMap.erase(key);
-            }
-
-            cpuInfoMap.insert_or_assign(
-                cpu, std::make_shared<CPUInfo>(*conn, cpu, peciAddress, i2cBus,
-                                               i2cDevice));
-
-            getProcessorInfo(io, conn, cpu);
+            return;
         }
+
+        if (!i2cDevice)
+        {
+            i2cDevice = defaultI2cSlaveAddr0 + *cpu - 1;
+        }
+
+        auto key = cpuInfoMap.find(*cpu);
+
+        if (key != cpuInfoMap.end())
+        {
+            cpuInfoMap.erase(key);
+        }
+
+        cpuInfoMap.emplace(*cpu, std::make_shared<CPUInfo>(*cpu, *peciAddress,
+                                                           i2cBus, *i2cDevice));
+
+        tryReadSSpec(conn, *cpu);
+
+#if PECI_ENABLED
+        getPPIN(io, conn, *cpu);
+#endif
     },
         service, object, "org.freedesktop.DBus.Properties", "GetAll",
         interface);
@@ -659,7 +664,9 @@
 
     cpu_info::hostStateSetup(conn);
 
+#if PECI_ENABLED
     cpu_info::sst::init();
+#endif
 
     // shared_ptr conn is global for the service
     // const reference of conn is passed to async calls
diff --git a/src/meson.build b/src/meson.build
index 620eae4..229842f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -36,15 +36,21 @@
   # i2c-tools provides no pkgconfig so we need to find it manually
   i2c_dep = cpp.find_library('i2c')
 
-  peci_dep = dependency('libpeci')
+  peci_dep = []
+  peci_flag = []
+  peci_files = []
+  if get_option('cpuinfo-peci').allowed()
+    peci_flag = '-DPECI_ENABLED=1'
+    peci_dep = dependency('libpeci')
+    peci_files = ['speed_select.cpp', 'sst_mailbox.cpp']
+  endif
 
   executable(
     'cpuinfoapp',
     'cpuinfo_main.cpp',
-    'speed_select.cpp',
-    'sst_mailbox.cpp',
     'cpuinfo_utils.cpp',
-    cpp_args: boost_args,
+    peci_files,
+    cpp_args: boost_args + peci_flag,
     dependencies: [
       boost_dep,
       sdbusplus_dep,