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/spi_device_code_updater.cpp b/bios-spi/spi_device_code_updater.cpp
new file mode 100644
index 0000000..888d5c8
--- /dev/null
+++ b/bios-spi/spi_device_code_updater.cpp
@@ -0,0 +1,157 @@
+#include "spi_device_code_updater.hpp"
+
+#include "common/include/software_manager.hpp"
+#include "spi_device.hpp"
+
+#include <gpiod.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/bus.hpp>
+#include <xyz/openbmc_project/ObjectMapper/client.hpp>
+
+SPIDeviceCodeUpdater::SPIDeviceCodeUpdater(sdbusplus::async::context& ctx,
+ bool isDryRun, bool debug) :
+ SoftwareManager(ctx, configTypeSPIDevice, isDryRun), debug(debug)
+{}
+
+// NOLINTBEGIN
+sdbusplus::async::task<>
+ SPIDeviceCodeUpdater::getInitialConfigurationSingleDevice(
+ const std::string& service, const std::string& path,
+ DeviceConfig& config)
+// NOLINTEND
+{
+ std::optional<std::string> optSpiPath =
+ co_await SoftwareManager::dbusGetRequiredConfigurationProperty<
+ std::string>(service, path, "Path", config);
+
+ std::optional<bool> optHasME =
+ co_await SoftwareManager::dbusGetRequiredConfigurationProperty<bool>(
+ service, path, "HasME", config);
+
+ std::optional<std::string> optLayout =
+ co_await SoftwareManager::dbusGetRequiredConfigurationProperty<
+ std::string>(service, path, "Layout", config);
+
+ std::optional<std::string> optTool =
+ co_await SoftwareManager::dbusGetRequiredConfigurationProperty<
+ std::string>(service, path, "Tool", config);
+
+ if (!optSpiPath.has_value() || !optHasME.has_value())
+ {
+ co_return;
+ }
+
+ std::string spiPath = optSpiPath.value();
+ bool hasME = optHasME.value();
+
+ lg2::debug("[config] spi device: {SPIDEV}", "SPIDEV", spiPath);
+
+ std::vector<std::string> gpioLines;
+ std::vector<uint8_t> gpioValues;
+
+ std::string configIntfMuxGpios;
+
+ std::vector<std::string> configIntfs = {
+ "xyz.openbmc_project.Configuration." + configTypeSPIDevice};
+
+ for (auto& iface : configIntfs)
+ {
+ configIntfMuxGpios = iface + ".MuxGpios";
+ }
+
+ for (size_t i = 0; true; i++)
+ {
+ std::string intf = configIntfMuxGpios + std::to_string(i);
+ std::optional<std::string> optGpioName =
+ co_await SoftwareManager::dbusGetRequiredProperty<std::string>(
+ service, path, intf, "Name");
+ std::optional<std::string> optGpioPolarity =
+ co_await SoftwareManager::dbusGetRequiredProperty<std::string>(
+ service, path, intf, "Polarity");
+
+ if (!optGpioName.has_value() || !optGpioPolarity.has_value())
+ {
+ break;
+ }
+
+ gpioLines.push_back(optGpioName.value());
+ gpioValues.push_back((optGpioPolarity.value() == "High") ? 1 : 0);
+
+ lg2::debug("[config] gpio {NAME} = {VALUE}", "NAME",
+ optGpioName.value(), "VALUE", optGpioPolarity.value());
+ }
+
+ lg2::debug("[config] extracted {N} gpios from EM config", "N",
+ gpioLines.size());
+
+ bool layoutFlat;
+
+ if (!optLayout.has_value())
+ {
+ lg2::info("[config] error: no flash layout chosen (property 'Layout')");
+ co_return;
+ }
+
+ const std::string& layout = optLayout.value();
+ if (layout == "Flat")
+ {
+ layoutFlat = true;
+ }
+ else if (layout == "IFD")
+ {
+ layoutFlat = false;
+ }
+ else
+ {
+ lg2::error("[config] unsupported flash layout config: {OPTION}",
+ "OPTION", layout);
+ lg2::info("supported options: 'Flat', 'IFD'");
+ co_return;
+ }
+
+ bool toolFlashrom;
+ if (!optTool.has_value())
+ {
+ lg2::error("[config] error: no tool chose (property 'Tool')");
+ co_return;
+ }
+
+ const std::string& tool = optTool.value();
+
+ if (tool == "flashrom")
+ {
+ toolFlashrom = true;
+ }
+ else if (tool == "None")
+ {
+ toolFlashrom = false;
+ }
+ else
+ {
+ lg2::error("[config] unsupported Tool: {OPTION}", "OPTION", tool);
+ co_return;
+ }
+
+ auto spiDevice = std::make_unique<SPIDevice>(
+ ctx, spiPath, dryRun, hasME, gpioLines, gpioValues, config, this,
+ layoutFlat, toolFlashrom, this->debug);
+
+ // we do not know the version on startup, it becomes known on update
+ std::string version = "unknown";
+
+ std::unique_ptr<Software> bsws =
+ std::make_unique<Software>(ctx, "spi_swid_unknown", *spiDevice);
+
+ bsws->setVersion("unknown");
+
+ // enable this software to be updated
+ std::set<RequestedApplyTimes> allowedApplyTimes = {
+ RequestedApplyTimes::Immediate, RequestedApplyTimes::OnReset};
+
+ bsws->enableUpdate(allowedApplyTimes);
+
+ spiDevice->softwareCurrent = std::move(bsws);
+
+ devices.insert(std::move(spiDevice));
+}