BMC Minimum ship Level
This code compares the BMC_MSL defined at compile time,
It will parse the new fw (explicit at MANIFEST file) and use regex to
evaluate it and compare BMC_MSL against version from MANIFEST.
If newer or equal it will apply, otherwise it will fail,
preventing activation operation.
Tested:
regex-bmc-msl="([a-z]+[0-9]{2})+([0-9]+).([0-9]+).([0-9]+)"
fw-package="version=fw1010.00-28.4-0-ge611abca21"
bmc-msl="fw1010.00-27" proceeds with activation...
bmc-msl="fw1010.00-29" returns:
Jul 15 20:35:45 tacoma1z-w81 phosphor-image-updater[766]:
BMC Minimum Ship Level NOT met
Jul 15 20:35:45 tacoma1z-w81 phosphor-image-updater[766]:
A system component has a software version that is incompatible as
determined by the implementation and needs to be updated....
Signed-off-by: Miguel Gomez <mgomez@mx1.ibm.com>
Change-Id: I0ab0eba7c7c89f38ca698aa3e369aa50797edb07
diff --git a/activation.cpp b/activation.cpp
index 6b9dfb8..79b5b10 100644
--- a/activation.cpp
+++ b/activation.cpp
@@ -2,6 +2,7 @@
#include "images.hpp"
#include "item_updater.hpp"
+#include "msl_verify.hpp"
#include "serialize.hpp"
#include <phosphor-logging/elog-errors.hpp>
@@ -9,6 +10,7 @@
#include <phosphor-logging/log.hpp>
#include <sdbusplus/exception.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/Software/Version/error.hpp>
#ifdef WANT_SIGNATURE_VERIFY
#include "image_verify.hpp"
@@ -78,7 +80,6 @@
auto Activation::activation(Activations value) -> Activations
{
-
if ((value != softwareServer::Activation::Activations::Active) &&
(value != softwareServer::Activation::Activations::Activating))
{
@@ -111,6 +112,24 @@
}
#endif
+ auto versionStr = parent.versions.find(versionId)->second->version();
+
+ if (!minimum_ship_level::verify(versionStr))
+ {
+ using namespace phosphor::logging;
+ using IncompatibleErr = sdbusplus::xyz::openbmc_project::Software::
+ Version::Error::Incompatible;
+ using Incompatible =
+ xyz::openbmc_project::Software::Version::Incompatible;
+
+ report<IncompatibleErr>(
+ prev_entry<Incompatible::MIN_VERSION>(),
+ prev_entry<Incompatible::ACTUAL_VERSION>(),
+ prev_entry<Incompatible::VERSION_PURPOSE>());
+ return softwareServer::Activation::activation(
+ softwareServer::Activation::Activations::Failed);
+ }
+
#ifdef WANT_SIGNATURE_VERIFY
fs::path uploadDir(IMG_UPLOAD_DIR);
if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH))
diff --git a/meson.build b/meson.build
index 1923fb8..c74a927 100644
--- a/meson.build
+++ b/meson.build
@@ -72,6 +72,9 @@
conf.set_quoted('SIGNED_IMAGE_CONF_PATH', get_option('signed-image-conf-path'))
conf.set_quoted('SYNC_LIST_DIR_PATH', get_option('sync-list-dir-path'))
conf.set_quoted('SYNC_LIST_FILE_NAME', get_option('sync-list-file-name'))
+conf.set_quoted('BMC_MSL', get_option('bmc-msl'))
+conf.set_quoted('REGEX_BMC_MSL', get_option('regex-bmc-msl'))
+
configure_file(output: 'config.h', configuration: conf)
@@ -108,7 +111,8 @@
'item_updater_main.cpp',
'serialize.cpp',
'version.cpp',
- 'utils.cpp'
+ 'utils.cpp',
+ 'msl_verify.cpp'
)
if get_option('bmc-layout').contains('static')
diff --git a/meson_options.txt b/meson_options.txt
index c0def0c..0043434 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -89,3 +89,15 @@
value: 'synclist',
description: 'The name of the sync list file.',
)
+
+option(
+ 'bmc-msl', type: 'string',
+ value: '',
+ description: 'The BMC minimum ship level.',
+)
+
+option(
+ 'regex-bmc-msl', type: 'string',
+ value: '',
+ description: 'The Regular expression to parse the MSL.',
+)
diff --git a/msl_verify.cpp b/msl_verify.cpp
new file mode 100644
index 0000000..3cd2278
--- /dev/null
+++ b/msl_verify.cpp
@@ -0,0 +1,96 @@
+#include "config.h"
+
+#include "msl_verify.hpp"
+
+#include "version.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+#include <regex>
+
+using namespace phosphor::logging;
+
+int minimum_ship_level::compare(const Version& versionToCompare,
+ const Version& mslVersion)
+{
+ if (versionToCompare.major > mslVersion.major)
+ return (1);
+ if (versionToCompare.major < mslVersion.major)
+ return (-1);
+
+ if (versionToCompare.minor > mslVersion.minor)
+ return (1);
+ if (versionToCompare.minor < mslVersion.minor)
+ return (-1);
+
+ if (versionToCompare.rev > mslVersion.rev)
+ return (1);
+ if (versionToCompare.rev < mslVersion.rev)
+ return (-1);
+
+ // Both string are equal and there is no need to make an upgrade return 0.
+ return 0;
+}
+
+// parse Function copy inpVersion onto outVersion in Version format
+// {major,minor,rev}.
+void minimum_ship_level::parse(const std::string& inpVersion,
+ Version& outVersion)
+{
+ std::smatch match;
+ outVersion = {0, 0, 0};
+
+ std::regex rx{REGEX_BMC_MSL, std::regex::extended};
+
+ if (!std::regex_search(inpVersion, match, rx))
+ {
+ log<level::ERR>("Unable to parse BMC version",
+ entry("VERSION=%s", inpVersion.c_str()));
+ return;
+ }
+
+ outVersion.major = std::stoi(match[2]);
+ outVersion.minor = std::stoi(match[3]);
+ outVersion.rev = std::stoi(match[4]);
+}
+
+bool minimum_ship_level::verify(const std::string& versionManifest)
+{
+
+ // If there is no msl or mslRegex return upgrade is needed.
+ std::string msl{BMC_MSL};
+ std::string mslRegex{REGEX_BMC_MSL};
+ if (msl.empty() || mslRegex.empty())
+ {
+ return true;
+ }
+
+ // Define mslVersion variable and populate in Version format
+ // {major,minor,rev} using parse function.
+
+ Version mslVersion = {0, 0, 0};
+ parse(msl, mslVersion);
+
+ // Define actualVersion variable and populate in Version format
+ // {major,minor,rev} using parse function.
+ std::string tmpStr{};
+
+ tmpStr = versionManifest;
+ Version actualVersion = {0, 0, 0};
+ parse(versionManifest, actualVersion);
+
+ // Compare actualVersion vs MSL.
+ auto rc = compare(actualVersion, mslVersion);
+ if (rc < 0)
+ {
+ log<level::ERR>(
+ "BMC Minimum Ship Level NOT met",
+ entry("MIN_VERSION=%s", msl.c_str()),
+ entry("ACTUAL_VERSION=%s", tmpStr.c_str()),
+ entry("VERSION_PURPOSE=%s",
+ "xyz.openbmc_project.Software.Version.VersionPurpose.BMC"));
+ return false;
+ }
+
+ return true;
+}
diff --git a/msl_verify.hpp b/msl_verify.hpp
new file mode 100644
index 0000000..9fd6695
--- /dev/null
+++ b/msl_verify.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <string>
+
+namespace minimum_ship_level
+{
+
+/** @brief Version components */
+struct Version
+{
+ uint8_t major;
+ uint8_t minor;
+ uint8_t rev;
+};
+
+/** @brief Verify if the current BMC version meets the min ship level
+ * @return true if the verification succeeded, false otherwise
+ */
+bool verify(const std::string& versionStr);
+
+/** @brief Parse the version components into a struct
+ * @details User passes a version string in regex format (REGEX_BMC_MSL)
+ * at compilation time, this value is break down by parse function to allocate
+ * a struct so it can be compared position by position against the (BMC_MSL)
+ * also defined at compile time.
+ * @param[in] versionStr - The version string to be parsed
+ * @param[out] version - The version struct to be populated
+ */
+void parse(const std::string& versionStr, Version& version);
+
+/** @brief Compare the versions provided
+ * @param[in] a - The first version to compare
+ * @param[in] b - The second version to compare
+ * @return 1 if a > b
+ * 0 if a = b
+ * -1 if a < b
+ */
+int compare(const Version& a, const Version& b);
+
+} // namespace minimum_ship_level