fw update: implement example device
Introduce example-device and example-code-updater, which are used as
examples for how to implement devices and code updaters according to the
design [1]
Tested:
The example code updater allows us to perform manual testing without
a dependency on any specific device.
Running the example code updater results in follow dbus output:
```
busctl --full --no-pager tree xyz.openbmc_project.ExampleCodeUpdater
└─ /xyz
└─ /xyz/openbmc_project
└─ /xyz/openbmc_project/software
└─ /xyz/openbmc_project/software/ExampleSoftware_4081
busctl --full --no-pager introspect xyz.openbmc_project.ExampleCodeUpdater /xyz/openbmc_project/software/ExampleSoftware_4081
CodeUpdater /xyz/openbmc_project/software/ExampleSoftware_4081
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
...
xyz.openbmc_project.Software.Activation interface - - -
.Activation property s "xyz.openbmc_project.Software.Activation.Activations.Active" emits-change writable
.RequestedActivation property s "xyz.openbmc_project.Software.Activation.RequestedActivations.None" emits-change writable
xyz.openbmc_project.Software.Update interface - - -
.StartUpdate method hs o -
.AllowedApplyTimes property as 1 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset" emits-change
xyz.openbmc_project.Software.Version interface - - -
.Purpose property s "xyz.openbmc_project.Software.Version.VersionPurpose.Unknown" emits-change writable
.Version property s "v1.0"
```
References:
[1] https://github.com/openbmc/docs/blob/master/designs/code-update.md
Change-Id: I2bad241b3102e58eda5139174791adda82f6ca95
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/test/common/exampledevice/example_device.cpp b/test/common/exampledevice/example_device.cpp
new file mode 100644
index 0000000..59acd39
--- /dev/null
+++ b/test/common/exampledevice/example_device.cpp
@@ -0,0 +1,96 @@
+#include "example_device.hpp"
+
+#include "common/include/device.hpp"
+#include "common/include/software_config.hpp"
+#include "common/include/software_manager.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Association/Definitions/server.hpp>
+#include <xyz/openbmc_project/Software/Update/server.hpp>
+
+#include <memory>
+
+PHOSPHOR_LOG2_USING;
+
+using namespace phosphor::software;
+using namespace phosphor::software::config;
+using namespace phosphor::software::manager;
+using namespace phosphor::software::device;
+using namespace phosphor::software::example_device;
+
+SoftwareConfig ExampleDevice::defaultConfig =
+ SoftwareConfig(exampleInvObjPath, exampleVendorIANA,
+ exampleCompatibleHardware, "Nop", exampleName);
+
+long ExampleCodeUpdater::getRandomId()
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ unsigned int seed = ts.tv_nsec ^ getpid();
+ srandom(seed);
+ return random() % 10000;
+}
+
+// nop code updater needs unique suffix on dbus for parallel unit testing
+ExampleCodeUpdater::ExampleCodeUpdater(sdbusplus::async::context& ctx,
+ long uniqueSuffix) :
+ SoftwareManager(ctx, "ExampleUpdater" + std::to_string(uniqueSuffix))
+{}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> ExampleCodeUpdater::initDevice(
+ const std::string& /*unused*/, const std::string& /*unused*/,
+ SoftwareConfig& /*unused*/)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ auto device = std::make_unique<ExampleDevice>(ctx, this);
+
+ device->softwareCurrent = std::make_unique<Software>(ctx, *device);
+
+ device->softwareCurrent->setVersion("v1.0");
+ device->softwareCurrent->setActivation(
+ SoftwareActivation::Activations::Active);
+
+ auto applyTimes = {RequestedApplyTimes::OnReset};
+ device->softwareCurrent->enableUpdate(applyTimes);
+
+ devices.insert({exampleInvObjPath, std::move(device)});
+
+ co_return true;
+}
+
+ExampleDevice::ExampleDevice(sdbusplus::async::context& ctx,
+ SoftwareManager* parent,
+ const SoftwareConfig& config) :
+ Device(ctx, config, parent,
+ {RequestedApplyTimes::Immediate, RequestedApplyTimes::OnReset})
+{}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> ExampleDevice::updateDevice(
+ const uint8_t* /*unused*/, size_t compImageSize)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ debug("Called device specific update function with image size {SIZE}",
+ "SIZE", compImageSize);
+
+ deviceSpecificUpdateFunctionCalled = true;
+
+ // Setting this property for demonstration purpose.
+ // For a real device, this could represent the
+ // percentage completion of writing the firmware,
+ // and any progress made in the update process within this function.
+ // There is no hard constraint on the values here,
+ // we do not have to reach any specific percentage.
+ // The percentage should be monotonic and increasing.
+ for (auto progress = 0; progress <= 100; progress += 20)
+ {
+ setUpdateProgress(90);
+ }
+
+ co_return true;
+}