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/software.cpp b/common/src/software.cpp
new file mode 100644
index 0000000..473e377
--- /dev/null
+++ b/common/src/software.cpp
@@ -0,0 +1,196 @@
+#include "software.hpp"
+
+#include "device.hpp"
+#include "software_update.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async/context.hpp>
+#include <xyz/openbmc_project/Association/Definitions/server.hpp>
+#include <xyz/openbmc_project/Software/Activation/aserver.hpp>
+#include <xyz/openbmc_project/Software/Update/aserver.hpp>
+#include <xyz/openbmc_project/State/Host/client.hpp>
+
+SoftwareActivationProgress::SoftwareActivationProgress(
+ sdbusplus::async::context& ctx, const char* objPath) :
+ ActivationProgress(ctx, objPath)
+{
+ // This prevents "Conditional jump or move depends on uninitialised
+ // value(s)"
+ // when properties are updated for the first time
+ progress_ = 0;
+}
+
+Software::Software(sdbusplus::async::context& ctx, const std::string& swid,
+ Device& parent) :
+ sdbusplus::aserver::xyz::openbmc_project::software::Activation<Software>(
+ ctx, Software::getObjPathFromSwid(swid).c_str()),
+ swid(swid), parent(parent), ctx(ctx)
+{
+ // initialize the members of our base class to prevent
+ // "Conditional jump or move depends on uninitialised value(s)"
+ activation_ = Activations::NotReady;
+ requested_activation_ = RequestedActivations::None;
+
+ std::string objPath = Software::getObjPathFromSwid(swid);
+
+ if (!objPath.starts_with("/"))
+ {
+ throw std::invalid_argument(objPath + " is not an object path");
+ }
+
+ lg2::debug("{SWID}: created dbus interfaces on path {OBJPATH}", "SWID",
+ swid, "OBJPATH", objPath);
+};
+
+std::string Software::getObjPathFromSwid(const std::string& swid)
+{
+ std::string basepath = "/xyz/openbmc_project/software/";
+ return basepath + swid;
+}
+
+static long int getRandomId()
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ unsigned int seed = ts.tv_nsec ^ getpid();
+ srandom(seed);
+ return random() % 10000;
+}
+
+std::string Software::getRandomSoftwareId(Device& parent)
+{
+ // Swid = <DeviceX>_<RandomId>
+ // For same type devices, extend the Dbus path to specify device
+ // instance, for example,
+ // /xyz/openbmc_project/Software/<deviceX>_<InstanceNum>_<SwId>
+
+ // The problem here is that InstanceNum needs to always stay the same,
+ // so that the device can be identified in the redfish fw inventory.
+
+ // Since 'Name' property is already provided in EM config, we can insert
+ // that in place of 'InstanceNum'.
+
+ const std::string configType = parent.getEMConfigType();
+
+ // 'Name' property in EM config
+ const std::string nameEM = parent.config.configName;
+
+ return std::format("{}_{}_{}", configType, nameEM, getRandomId());
+}
+
+Device& Software::getParentDevice()
+{
+ return this->parent;
+}
+
+// NOLINTBEGIN
+sdbusplus::async::task<> Software::setAssociationDefinitionsRunningActivating(
+ bool isRunning, bool isActivating)
+// NOLINTEND
+{
+ lg2::debug("{SWID}: setting association definitions", "SWID", this->swid);
+
+ std::string endpoint = co_await parent.getInventoryItemObjectPath();
+
+ if (!this->optSoftwareAssociationDefinitions)
+ {
+ std::string path = this->getObjectPath();
+ this->optSoftwareAssociationDefinitions =
+ std::make_unique<SoftwareAssociationDefinitions>(ctx, path.c_str());
+ }
+
+ std::string forward;
+ std::string reverse;
+ std::vector<std::tuple<std::string, std::string, std::string>> assocs;
+
+ if (isRunning)
+ {
+ lg2::debug("{SWID}: creating 'running' association to {OBJPATH}",
+ "SWID", this->swid, "OBJPATH", endpoint);
+ forward = "running";
+ reverse = "ran_on";
+ std::tuple<std::string, std::string, std::string> assocRunning = {
+ forward, reverse, endpoint};
+ assocs.push_back(assocRunning);
+ }
+
+ if (isActivating)
+ {
+ lg2::debug("{SWID}: creating 'activating' association to {OBJPATH}",
+ "SWID", this->swid, "OBJPATH", endpoint);
+ forward = "activating";
+ reverse = "activated_on";
+ std::tuple<std::string, std::string, std::string> assocActivating = {
+ forward, reverse, endpoint};
+ assocs.push_back(assocActivating);
+ }
+
+ this->optSoftwareAssociationDefinitions->associations(assocs);
+
+ co_return;
+}
+
+void Software::setVersion(const std::string& versionStr)
+{
+ lg2::debug("{SWID}: set version {VERSION}", "SWID", this->swid, "VERSION",
+ versionStr);
+
+ if (this->optSoftwareVersion)
+ {
+ lg2::error("{SWID}: version was already set", "SWID", this->swid);
+ return;
+ }
+
+ std::string path = this->getObjectPath();
+ this->optSoftwareVersion =
+ std::make_unique<SoftwareVersion>(ctx, path.c_str());
+ this->optSoftwareVersion->version(versionStr);
+}
+
+void Software::setActivationBlocksTransition(bool enabled)
+{
+ if (!enabled)
+ {
+ this->optActivationBlocksTransition = nullptr;
+ return;
+ }
+
+ std::string path = this->getObjectPath();
+ this->optActivationBlocksTransition =
+ std::make_unique<sdbusplus::aserver::xyz::openbmc_project::software::
+ ActivationBlocksTransition<Software>>(
+ this->ctx, path.c_str());
+}
+
+void Software::setActivation(
+ sdbusplus::common::xyz::openbmc_project::software::Activation::Activations
+ act)
+{
+ this->activation(act);
+}
+
+sdbusplus::message::object_path Software::getObjectPath() const
+{
+ std::string objPathStr = Software::getObjPathFromSwid(swid);
+ return sdbusplus::message::object_path(objPathStr.c_str());
+}
+
+void Software::enableUpdate(
+ const std::set<RequestedApplyTimes>& allowedApplyTimes)
+{
+ std::string objPath = getObjectPath();
+
+ if (this->optSoftwareUpdate != nullptr)
+ {
+ lg2::error("[Software] update of {OBJPATH} has already been enabled",
+ "OBJPATH", objPath);
+ return;
+ }
+
+ lg2::info(
+ "[Software] enabling update of {OBJPATH} (adding the update interface)",
+ "OBJPATH", objPath);
+
+ optSoftwareUpdate = std::make_unique<SoftwareUpdate>(
+ this->ctx, objPath.c_str(), *this, allowedApplyTimes);
+}