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/src/device.cpp b/common/src/device.cpp
new file mode 100644
index 0000000..49c9b1e
--- /dev/null
+++ b/common/src/device.cpp
@@ -0,0 +1,182 @@
+#include "device.hpp"
+
+#include "common/pldm/pldm_package_util.hpp"
+#include "software.hpp"
+#include "software_manager.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/async/context.hpp>
+#include <sdbusplus/bus.hpp>
+#include <xyz/openbmc_project/Association/Definitions/server.hpp>
+#include <xyz/openbmc_project/State/Host/client.hpp>
+
+#include <utility>
+
+const auto applyTimeImmediate = sdbusplus::common::xyz::openbmc_project::
+    software::ApplyTime::RequestedApplyTimes::Immediate;
+
+Device::Device(sdbusplus::async::context& ctx, bool isDryRun,
+               const DeviceConfig& config, SoftwareManager* parent) :
+    config(config), parent(parent), dryRun(isDryRun), ctx(ctx)
+{}
+
+// NOLINTBEGIN
+sdbusplus::async::task<bool> Device::startUpdateAsync(
+    sdbusplus::message::unix_fd image, RequestedApplyTimes applyTime,
+    std::unique_ptr<Software> softwareUpdate)
+// NOLINTEND
+{
+    lg2::debug("starting the async update with memfd {FD}", "FD", image.fd);
+
+    size_t pldm_pkg_size;
+
+    void* pldm_pkg = pldm_package_util::mmapImagePackage(image, &pldm_pkg_size);
+
+    if (pldm_pkg == NULL)
+    {
+        co_return false;
+    }
+
+    lg2::debug("[Device] mmapped the pldm update package");
+
+    std::shared_ptr<PackageParser> pp = pldm_package_util::parsePLDMPackage(
+        static_cast<uint8_t*>(pldm_pkg), pldm_pkg_size);
+
+    if (pp == nullptr)
+    {
+        lg2::error("could not parse PLDM package");
+        co_return false;
+    }
+
+    const bool success = co_await continueUpdateWithMappedPackage(
+        pldm_pkg, pp, applyTime, softwareUpdate);
+
+    if (success)
+    {
+        lg2::info("deleting old sw version {SWID}", "SWID",
+                  this->softwareCurrent->swid);
+
+        this->softwareCurrent = std::move(softwareUpdate);
+
+        lg2::info("new current sw version: {SWID}", "SWID",
+                  this->softwareCurrent->swid);
+    }
+    else
+    {
+        lg2::info("update failed, deleting sw update version {SWID}", "SWID",
+                  softwareUpdate->swid);
+    }
+
+    softwareUpdate = nullptr;
+
+    if (munmap(pldm_pkg, pldm_pkg_size) != 0)
+    {
+        lg2::error("[Device] failed to munmap the pldm package");
+    }
+
+    if (close(image.fd) != 0)
+    {
+        lg2::error("[Device] failed to close file descriptor {FD}", "FD",
+                   image.fd);
+    }
+
+    co_return success;
+}
+
+std::string Device::getEMConfigType() const
+{
+    return this->config.configType;
+}
+
+void Device::resetDevice()
+{
+    lg2::info("[Device] default implementation for reset device (nop)");
+}
+
+std::set<RequestedApplyTimes> Device::allowedApplyTimes()
+{
+    return {RequestedApplyTimes::Immediate, RequestedApplyTimes::OnReset};
+}
+
+// NOLINTBEGIN
+sdbusplus::async::task<bool> Device::continueUpdateWithMappedPackage(
+    void* pldm_pkg, const std::shared_ptr<PackageParser>& packageParser,
+    sdbusplus::common::xyz::openbmc_project::software::ApplyTime::
+        RequestedApplyTimes applyTime,
+    const std::unique_ptr<Software>& softwareUpdate)
+// NOLINTEND
+{
+    int status = 0;
+
+    // extract the component image for the specific device
+    size_t matchingComponentImageSize;
+    uint32_t matchingComponentOffset;
+    status = pldm_package_util::extractMatchingComponentImage(
+        packageParser, config.compatibleHardware, config.vendorIANA,
+        &matchingComponentOffset, &matchingComponentImageSize);
+
+    if (status != 0)
+    {
+        lg2::error("could not extract matching component image");
+
+        softwareUpdate->setActivation(
+            ActivationInterface::Activations::Invalid);
+
+        co_return false;
+    }
+
+    const uint8_t* matchingComponentImage =
+        static_cast<uint8_t*>(pldm_pkg) + matchingComponentOffset;
+
+    softwareUpdate->setActivation(ActivationInterface::Activations::Ready);
+
+    softwareUpdate->setVersion(packageParser->pkgVersion);
+
+    std::string objPath = softwareUpdate->getObjectPath();
+
+    softwareUpdate->optSoftwareActivationProgress =
+        std::make_unique<SoftwareActivationProgress>(ctx, objPath.c_str());
+
+    softwareUpdate->setActivationBlocksTransition(true);
+
+    softwareUpdate->setActivation(ActivationInterface::Activations::Activating);
+
+    bool success = co_await updateDevice(
+        matchingComponentImage, matchingComponentImageSize,
+        softwareUpdate->optSoftwareActivationProgress);
+
+    if (success)
+    {
+        softwareUpdate->setActivation(ActivationInterface::Activations::Active);
+    }
+
+    softwareUpdate->setActivationBlocksTransition(false);
+
+    softwareUpdate->optSoftwareActivationProgress = nullptr;
+
+    if (!success)
+    {
+        // do not apply the update, it has failed.
+        // We can delete the new software version.
+
+        co_return false;
+    }
+
+    if (applyTime == applyTimeImmediate)
+    {
+        this->resetDevice();
+
+        co_await softwareUpdate->setAssociationDefinitionsRunningActivating(
+            true, false);
+
+        softwareUpdate->enableUpdate(this->allowedApplyTimes());
+    }
+    else
+    {
+        co_await softwareUpdate->setAssociationDefinitionsRunningActivating(
+            false, true);
+    }
+
+    co_return true;
+}