fw update: spi device code updater

This code updater is for updating spi flash devices.
It can for example update the host firmware on different server boards
and has following features:

- power down the host before update
- set mux gpios to access spi flash
- (very limited) communication with ME (Management Engine)
- use flashrom to utilize fw with IFD (Intel Flash Descriptor)
- otherwise directly write to the flash chip.

The behavior of this code updater can be configured via EM.

Tested: on Tyan S5549 Board

Change-Id: I27803b7fded71af2364c2f55fad841a410603dec
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/bios-spi/main.cpp b/bios-spi/main.cpp
new file mode 100644
index 0000000..0a63d92
--- /dev/null
+++ b/bios-spi/main.cpp
@@ -0,0 +1,136 @@
+#include "spi_device_code_updater.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 <fstream>
+#include <iostream>
+
+// implementing the design
+// https://github.com/openbmc/docs/blob/377ed14df4913010752ee2faff994a50e12a6316/designs/code-update.md
+
+// NOLINTNEXTLINE
+sdbusplus::async::task<> startManualUpdate(sdbusplus::async::context& ctx,
+                                           SPIDeviceCodeUpdater& spidcu,
+                                           const std::string& imageFilename)
+{
+    if (spidcu.devices.empty())
+    {
+        lg2::error("no device available for manual update");
+        co_return;
+    }
+
+    const std::unique_ptr<Device>& device = *spidcu.devices.begin();
+
+    std::ifstream file(imageFilename, std::ios::binary | std::ios::ate);
+
+    if (!file.good())
+    {
+        lg2::error("error opening file {FILENAME}", "FILENAME", imageFilename);
+        co_return;
+    }
+
+    std::streamsize size = file.tellg();
+    file.seekg(0, std::ios::beg);
+
+    auto buffer = std::make_unique<uint8_t[]>(size);
+
+    if (!file.read(reinterpret_cast<char*>(buffer.get()), size))
+    {
+        throw std::runtime_error("Error reading file: " + imageFilename);
+    }
+
+    // TODO: find the proper object path here
+    auto sap =
+        std::make_unique<SoftwareActivationProgress>(ctx, "/dummyActivation");
+
+    co_await device->updateDevice(buffer.get(), size, sap);
+
+    co_return;
+}
+
+// NOLINTNEXTLINE
+sdbusplus::async::task<> start(sdbusplus::async::context& ctx,
+                               SPIDeviceCodeUpdater& spidcu, bool manual,
+                               const std::string& imageFilename)
+{
+    std::vector<std::string> configIntfs = {
+        "xyz.openbmc_project.Configuration." + configTypeSPIDevice};
+
+    co_await spidcu.getInitialConfiguration(configIntfs);
+
+    if (manual)
+    {
+        co_await startManualUpdate(ctx, spidcu, imageFilename);
+    }
+
+    co_return;
+}
+
+void printHelpText()
+{
+    std::cout << "--help              : print help" << std::endl;
+    std::cout << "--manual            : start a manual update" << std::endl;
+    std::cout << "--image <filename>  : filename for manual update"
+              << std::endl;
+}
+
+int main(int argc, char* argv[])
+{
+    // getting a really unspecific error from clang-tidy here
+    // about an uninitialized / garbage branch. Happy to discuss.
+
+    // NOLINTBEGIN
+
+    sdbusplus::async::context ctx;
+
+    bool manualUpdate = false;
+    bool printHelp = false;
+    bool dryRun = false;
+    bool debug = false;
+    std::string imageFilename = "";
+
+    for (int i = 1; i < argc; i++)
+    {
+        std::string arg = std::string(argv[i]);
+        if (arg == "--manual")
+        {
+            manualUpdate = true;
+        }
+        if (arg == "--image" && i < argc - 1)
+        {
+            imageFilename = std::string(argv[i + 1]);
+            i++;
+        }
+        if (arg == "--help")
+        {
+            printHelp = true;
+        }
+        if (arg == "--dryrun")
+        {
+            dryRun = true;
+        }
+        if (arg == "-debug")
+        {
+            debug = true;
+        }
+    }
+
+    if (printHelp)
+    {
+        printHelpText();
+    }
+
+    SPIDeviceCodeUpdater spidcu(ctx, dryRun, debug);
+
+    ctx.spawn(start(ctx, spidcu, manualUpdate, imageFilename));
+
+    ctx.run();
+
+    // NOLINTEND
+
+    return 0;
+}