fw update: common code

new daemons to implement the flow as described in
https://github.com/openbmc/docs/blob/master/designs/code-update.md

- common/
  common code folder
  - common update flow
  - base class for the device specific update daemons

The new daemons are all following the generic template of Code Updater
daemon as outlined in the design.

The idea is that they are separate daemons (per device, as outlined in
the design) but share all the code that's not device specific.

Tested: next patch in series

Change-Id: If2438b8506aceb8c5313ec13a0bf7cb68f3cc279
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/common/include/device.hpp b/common/include/device.hpp
new file mode 100644
index 0000000..56fc920
--- /dev/null
+++ b/common/include/device.hpp
@@ -0,0 +1,123 @@
+#pragma once
+
+#include "common/pldm/package_parser.hpp"
+#include "software.hpp"
+#include "software_config.hpp"
+
+#include <sdbusplus/async/context.hpp>
+#include <xyz/openbmc_project/Association/Definitions/aserver.hpp>
+#include <xyz/openbmc_project/Software/Activation/aserver.hpp>
+#include <xyz/openbmc_project/Software/Update/aserver.hpp>
+#include <xyz/openbmc_project/Software/Version/aserver.hpp>
+
+#include <string>
+
+using ActivationInterface =
+    sdbusplus::common::xyz::openbmc_project::software::Activation;
+
+namespace phosphor::software::manager
+{
+class SoftwareManager;
+};
+
+namespace phosphor::software::device
+{
+
+class Device
+{
+  public:
+    Device(sdbusplus::async::context& ctx,
+           const phosphor::software::config::SoftwareConfig& deviceConfig,
+           phosphor::software::manager::SoftwareManager* parent,
+           std::set<RequestedApplyTimes> allowedApplyTimes);
+
+    virtual ~Device() = default;
+    Device(const Device&) = delete;
+    Device& operator=(const Device&) = delete;
+    Device(Device&&) = delete;
+    Device& operator=(Device&&) = delete;
+
+    // @brief                      Applies the image to the device
+    // @param image                raw fw image without pldm header
+    // @param image_size           size of 'image'
+    // @returns                    true if update was applied successfully.
+    //                             Should also return true if update was applied
+    //                             successfully, but the host failed to power
+    //                             on.
+    virtual sdbusplus::async::task<bool> updateDevice(const uint8_t* image,
+                                                      size_t image_size) = 0;
+
+    // @brief               Set the ActivationProgress properties on dbus
+    // @param progress      progress value
+    // @returns             true on successful property update
+    bool setUpdateProgress(uint8_t progress) const;
+
+    // @brief                      This coroutine is spawned to perform the
+    // async update of the device.
+    // @param image                The memory fd with the pldm package
+    // @param applyTime            When the update should be applied
+    // @param swid                 The software id to use
+    // @returns                    true if update was successfull
+    sdbusplus::async::task<bool> startUpdateAsync(
+        sdbusplus::message::unix_fd image, RequestedApplyTimes applyTime,
+        std::unique_ptr<Software> softwareUpdateExternal);
+
+    // Value of 'Type' field for the configuration in EM exposes record
+    std::string getEMConfigType() const;
+
+  protected:
+    // The apply times for updates which are supported by the device
+    // Override this if your device deviates from the default set of apply
+    // times.
+    std::set<RequestedApplyTimes> allowedApplyTimes;
+
+    // software instance, identified by its swid
+    // The specific derived class also owns its dbus interfaces,
+    // which are destroyed when the instance is deleted.
+    std::unique_ptr<Software> softwareCurrent;
+
+    // In case of apply time == OnReset, this contains the software version
+    // which has been written to the device, or should be written to it,
+    // but is not active yet.
+    std::unique_ptr<Software> softwarePending;
+
+    // Resets the device, in whichever way is appropriate for the device.
+    // The reset must be capable to apply the firmware update which was done
+    // by 'deviceSpecificUpdateFunction', in case that function did not already
+    // apply it. This method is optional to implement for that reason.
+    virtual sdbusplus::async::task<bool> resetDevice();
+
+    // The common configuration that all devices share.
+    // We get this from EM configuration.
+    config::SoftwareConfig config;
+
+    manager::SoftwareManager* parent;
+
+    sdbusplus::async::context& ctx;
+
+  private:
+    bool updateInProgress = false;
+
+    // @param componentImage       component image as extracted from update pkg
+    // @param componentImageSize   size of 'componentImage'
+    // @param applyTime            when the update should be applied
+    // @param softwarePendingIn    the pending software instance
+    // @returns                    the return value of the device specific
+    // update function
+    sdbusplus::async::task<bool> continueUpdateWithMappedPackage(
+        const uint8_t* componentImage, size_t componentImageSize,
+        const std::string& componentVersion, RequestedApplyTimes applyTime,
+        const std::unique_ptr<Software>& softwarePendingIn);
+
+    // @brief     extracts the information we need from the pldm package
+    // @returns   true on success
+    sdbusplus::async::task<bool> getImageInfo(
+        std::unique_ptr<void, std::function<void(void*)>>& pldmPackage,
+        size_t pldmPackageSize, uint8_t** matchingComponentImage,
+        size_t* componentImageSize, std::string& componentVersion);
+
+    friend update::SoftwareUpdate;
+    friend Software;
+};
+
+}; // namespace phosphor::software::device
diff --git a/common/include/software.hpp b/common/include/software.hpp
new file mode 100644
index 0000000..09a0b0f
--- /dev/null
+++ b/common/include/software.hpp
@@ -0,0 +1,127 @@
+#pragma once
+
+#include "software_update.hpp"
+
+#include <sdbusplus/async/context.hpp>
+#include <xyz/openbmc_project/Association/Definitions/aserver.hpp>
+#include <xyz/openbmc_project/Software/Activation/aserver.hpp>
+#include <xyz/openbmc_project/Software/ActivationBlocksTransition/aserver.hpp>
+#include <xyz/openbmc_project/Software/ActivationProgress/aserver.hpp>
+#include <xyz/openbmc_project/Software/Version/aserver.hpp>
+
+#include <string>
+
+namespace phosphor::software::device
+{
+class Device;
+}
+
+namespace phosphor::software
+{
+
+// Need to declare this class to initialize the protected members of our base
+// class. This prevents "Conditional jump or move depends on uninitialised
+// value(s)" when properties are updated for the first time.
+class SoftwareActivationProgress :
+    private sdbusplus::aserver::xyz::openbmc_project::software::
+        ActivationProgress<Software>
+{
+  public:
+    SoftwareActivationProgress(sdbusplus::async::context& ctx,
+                               const char* objPath);
+
+    void setProgress(int progressArg);
+};
+
+using SoftwareActivationBlocksTransition = sdbusplus::aserver::xyz::
+    openbmc_project::software::ActivationBlocksTransition<Software>;
+
+using SoftwareVersion =
+    sdbusplus::aserver::xyz::openbmc_project::software::Version<Software>;
+using SoftwareActivation =
+    sdbusplus::aserver::xyz::openbmc_project::software::Activation<Software>;
+using SoftwareAssociationDefinitions =
+    sdbusplus::aserver::xyz::openbmc_project::association::Definitions<
+        Software>;
+
+// This represents a software version running on the device.
+class Software : private SoftwareActivation
+{
+  public:
+    Software(sdbusplus::async::context& ctx, device::Device& parent);
+
+    // Set the activation status of this software
+    // @param activation         The activation status
+    void setActivation(SoftwareActivation::Activations activation);
+
+    // Add / remove the 'ActivationBlocksTransition' dbus interface.
+    // This dbus interface is only needed during the update process.
+    // @param enabled       determines if the dbus interface should be there
+    void setActivationBlocksTransition(bool enabled);
+
+    // This should populate 'softwareUpdate'
+    // @param allowedApplyTimes        When updates to this Version can be
+    // applied
+    void enableUpdate(const std::set<RequestedApplyTimes>& allowedApplyTimes);
+
+    // This should populate 'softwareVersion'
+    // @param version      the version string
+    void setVersion(const std::string& versionStr);
+
+    // This should populate 'softwareAssociationDefinitions'
+    // @param isRunning             if the software version is currently running
+    // on the device. Otherwise the software is assumed to be activating (not
+    // yet running).
+    sdbusplus::async::task<> createInventoryAssociations(bool isRunning);
+
+    // object path of this software
+    sdbusplus::message::object_path objectPath;
+
+    // The device we are associated to, meaning this software is either running
+    // on the device, or could potentially run on that device (update).
+    device::Device& parentDevice;
+
+    // The software id
+    const std::string swid;
+
+    // This is only required during the activation of the new fw
+    // and is deleted again afterwards.
+    // This member is public since the device specific update function
+    // needs to update the progress.
+    std::unique_ptr<SoftwareActivationProgress> softwareActivationProgress =
+        nullptr;
+
+  protected:
+    // @returns        a random software id (swid) for that device
+    static std::string getRandomSoftwareId(device::Device& parent);
+
+  private:
+    Software(sdbusplus::async::context& ctx, device::Device& parent,
+             const std::string& swid);
+
+    // Dbus interface to prevent power state transition during update.
+    std::unique_ptr<SoftwareActivationBlocksTransition>
+        activationBlocksTransition = nullptr;
+
+    // The software update dbus interface is not always present.
+    // It is constructed if the software version is able to be updated.
+    // For the new software version, this interface is constructed after the
+    // update has taken effect
+    std::unique_ptr<update::SoftwareUpdate> updateIntf = nullptr;
+
+    // We do not know the software version until we parse the PLDM package.
+    // Since the Activation interface needs to be available
+    // before then, this is nullptr until we get to know the version.
+    std::unique_ptr<SoftwareVersion> version = nullptr;
+
+    // This represents our association to the inventory item in case
+    // this software is currently on the device.
+    std::unique_ptr<SoftwareAssociationDefinitions> associationDefinitions =
+        nullptr;
+
+    sdbusplus::async::context& ctx;
+
+    friend update::SoftwareUpdate;
+};
+
+}; // namespace phosphor::software
diff --git a/common/include/software_config.hpp b/common/include/software_config.hpp
new file mode 100644
index 0000000..f6d41ee
--- /dev/null
+++ b/common/include/software_config.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "sdbusplus/async/context.hpp"
+
+#include <cstdint>
+#include <string>
+
+namespace phosphor::software::device
+{
+class Device;
+}
+
+using namespace phosphor::software::device;
+
+namespace phosphor::software::config
+{
+
+/* This class represents the device software configuration we get from
+ * entity-manager via D-Bus. Each Code Updater can create its own configuration
+ * class that inherits from this to store additional properties from their
+ * device configuration, like bus/address/...
+ */
+class SoftwareConfig
+{
+  public:
+    SoftwareConfig(const std::string& objPath, uint32_t vendorIANA,
+                   const std::string& compatible, const std::string& configType,
+                   const std::string& name);
+
+    // The dbus object path this configuration was fetched from
+    const std::string objectPath;
+
+    // https://github.com/openbmc/entity-manager/blob/master/schemas/firmware.json
+
+    // 'Name' field from the EM config
+    const std::string configName;
+
+    // 'Type' field from the EM config
+    const std::string configType;
+
+    // @returns        the object path of the inventory item which
+    //                 can be associated with this device.
+    sdbusplus::async::task<std::string> getInventoryItemObjectPath(
+        sdbusplus::async::context& ctx);
+
+  private:
+    // 'VendorIANA' field from the EM config
+    const uint32_t vendorIANA; // e.g. "0x0000A015", 4 bytes as per PLDM spec
+
+    // 'CompatibleHardware' field from the EM config
+    const std::string
+        compatibleHardware; // e.g.
+                            // "com.meta.Hardware.Yosemite4.MedusaBoard.CPLD.LCMX02_2000HC"
+
+    friend Device;
+};
+
+}; // namespace phosphor::software::config
diff --git a/common/include/software_manager.hpp b/common/include/software_manager.hpp
new file mode 100644
index 0000000..7cc2e6a
--- /dev/null
+++ b/common/include/software_manager.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include "device.hpp"
+
+#include <boost/asio/steady_timer.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/async/context.hpp>
+#include <sdbusplus/timer.hpp>
+
+#include <string>
+
+using namespace phosphor::software::config;
+using namespace phosphor::software::device;
+
+namespace phosphor::software::manager
+{
+
+// This is the base class for the code updater
+// Every code updater can inherit from this
+class SoftwareManager
+{
+  public:
+    SoftwareManager(sdbusplus::async::context& ctx,
+                    const std::string& serviceNameSuffix);
+
+    // Fetches initial configuration from dbus and initializes devices.
+    // This should be called once by a code updater at startup.
+    // @param configurationInterfaces    the dbus interfaces from which to fetch
+    // configuration
+    sdbusplus::async::task<> initDevices(
+        const std::vector<std::string>& configurationInterfaces);
+
+    // Map of EM config object path to device.
+    std::map<sdbusplus::message::object_path, std::unique_ptr<Device>> devices;
+
+  protected:
+    // This function receives a dbus name and object path for a single device,
+    // which was configured.
+    // The component code updater overrides this function and may create a
+    // device instance internally, or reject the configuration as invalid.
+    // @param service       The dbus name where our configuration is
+    // @param config        The common configuration properties which are shared
+    // by all devices.
+    //                      Also includes the object path to fetch other
+    //                      configuration properties.
+    // @returns true        if the configuration was accepted
+    virtual sdbusplus::async::task<bool> initDevice(const std::string& service,
+                                                    const std::string& path,
+                                                    SoftwareConfig& config) = 0;
+
+    sdbusplus::async::context& ctx;
+
+  private:
+    // request the bus name on dbus after all configuration has been parsed
+    // and the devices have been initialized
+    // @returns        the name on dbus
+    std::string setupBusName();
+
+    // this is appended to the common prefix to construct the dbus name
+    std::string serviceNameSuffix;
+
+    sdbusplus::server::manager_t manager;
+
+    friend Software;
+    friend Device;
+};
+
+}; // namespace phosphor::software::manager
diff --git a/common/include/software_update.hpp b/common/include/software_update.hpp
new file mode 100644
index 0000000..406c282
--- /dev/null
+++ b/common/include/software_update.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <sdbusplus/async/context.hpp>
+#include <xyz/openbmc_project/Software/Update/aserver.hpp>
+
+namespace phosphor::software
+{
+class Software;
+};
+
+using RequestedApplyTimes = sdbusplus::common::xyz::openbmc_project::software::
+    ApplyTime::RequestedApplyTimes;
+
+namespace phosphor::software::update
+{
+
+class SoftwareUpdate :
+    public sdbusplus::aserver::xyz::openbmc_project::software::Update<
+        SoftwareUpdate>
+{
+  public:
+    SoftwareUpdate(sdbusplus::async::context& ctx, const char* path,
+                   Software& software,
+                   const std::set<RequestedApplyTimes>& allowedApplyTimes);
+
+    auto method_call(start_update_t su, auto image, auto applyTime)
+        -> sdbusplus::async::task<start_update_t::return_type>;
+
+    auto get_property(allowed_apply_times_t aat) const;
+
+  private:
+    Software& software;
+
+    const std::set<RequestedApplyTimes> allowedApplyTimes;
+};
+
+}; // namespace phosphor::software::update