Initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"a":{
|
||||
"cipher":3,
|
||||
"authentication":1,
|
||||
"integrity":1,
|
||||
"confidentiality":1
|
||||
},
|
||||
"b":{
|
||||
"cipher":17,
|
||||
"authentication":3,
|
||||
"integrity":4,
|
||||
"confidentiality":1
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
From aaf8a4a5b82baff679f557ed83b25af6ff2919cf Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Amelkin <a.amelkin@yadro.com>
|
||||
Date: Thu, 23 May 2019 20:39:57 +0300
|
||||
Subject: [PATCH] Add support for persistent-only settings
|
||||
|
||||
Some settings such as Boot Initiator Mailbox do not support
|
||||
one-time setting mode (as per IPMI 2.0 specification).
|
||||
|
||||
This commit adds support for such persistent-only settings.
|
||||
|
||||
Partially resolves openbmc/openbmc#3391
|
||||
|
||||
Change-Id: Iec8e2f5bddbc50d270916567effe334f10db2987
|
||||
Signed-off-by: Alexander Amelkin <a.amelkin@yadro.com>
|
||||
Signed-off-by: Ivan Mikhaylov <i.mikhaylov@yadro.com>
|
||||
---
|
||||
settings.cpp | 35 +++++++++++++++++++++++++++++++----
|
||||
1 file changed, 31 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/settings.cpp b/settings.cpp
|
||||
index 2fa2511..6002365 100644
|
||||
--- a/settings.cpp
|
||||
+++ b/settings.cpp
|
||||
@@ -95,19 +95,44 @@ namespace boot
|
||||
std::tuple<Path, OneTimeEnabled> setting(const Objects& objects,
|
||||
const Interface& iface)
|
||||
{
|
||||
- constexpr auto bootObjCount = 2;
|
||||
+ constexpr auto ambiguousOperationCount = 2;
|
||||
constexpr auto oneTime = "one_time";
|
||||
constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
|
||||
+ bool oneTimeEnabled = false;
|
||||
|
||||
const std::vector<Path>& paths = objects.map.at(iface);
|
||||
auto count = paths.size();
|
||||
- if (count != bootObjCount)
|
||||
+ if (!count)
|
||||
{
|
||||
- log<level::ERR>("Exactly two objects expected",
|
||||
+ // If there are no objects implementing the requested interface,
|
||||
+ // that must be an error.
|
||||
+ log<level::ERR>("Interface objects not found",
|
||||
+ entry("INTERFACE=%s", iface.c_str()));
|
||||
+ elog<InternalFailure>();
|
||||
+ }
|
||||
+ else if (count < ambiguousOperationCount)
|
||||
+ {
|
||||
+ // On the contrary, if there is just one object, that may mean
|
||||
+ // that this particular interface doesn't support one-time
|
||||
+ // setting mode (e.g. Boot Initiator Mailbox).
|
||||
+ // That is not an error, just return the regular setting.
|
||||
+ // If there's just one object, that's the only kind of setting
|
||||
+ // mode this interface supports, so just return that setting path.
|
||||
+ const Path& regularSetting = paths[0];
|
||||
+ return std::make_tuple(regularSetting, oneTimeEnabled);
|
||||
+ }
|
||||
+ else if (count > ambiguousOperationCount)
|
||||
+ {
|
||||
+ // Something must be wrong if there are more objects than expected
|
||||
+ log<level::ERR>("Exactly 1 or 2 interface objects are required",
|
||||
entry("INTERFACE=%s", iface.c_str()),
|
||||
entry("COUNT=%d", count));
|
||||
elog<InternalFailure>();
|
||||
}
|
||||
+
|
||||
+ // We are here because there were exactly two objects implementing the
|
||||
+ // same interface. Take those two and find out which of them is the
|
||||
+ // one-time setting, consider the other the persistent setting.
|
||||
size_t index = 0;
|
||||
if (std::string::npos == paths[0].rfind(oneTime))
|
||||
{
|
||||
@@ -116,6 +141,8 @@ std::tuple<Path, OneTimeEnabled> setting(const Objects& objects,
|
||||
const Path& oneTimeSetting = paths[index];
|
||||
const Path& regularSetting = paths[!index];
|
||||
|
||||
+ // Now see if the one-time setting is enabled and return the path for it
|
||||
+ // if so. Otherwise return the path for the persistent setting.
|
||||
auto method = objects.bus.new_method_call(
|
||||
objects.service(oneTimeSetting, iface).c_str(), oneTimeSetting.c_str(),
|
||||
ipmi::PROP_INTF, "Get");
|
||||
@@ -131,7 +158,7 @@ std::tuple<Path, OneTimeEnabled> setting(const Objects& objects,
|
||||
|
||||
std::variant<bool> enabled;
|
||||
reply.read(enabled);
|
||||
- auto oneTimeEnabled = std::get<bool>(enabled);
|
||||
+ oneTimeEnabled = std::get<bool>(enabled);
|
||||
const Path& setting = oneTimeEnabled ? oneTimeSetting : regularSetting;
|
||||
return std::make_tuple(setting, oneTimeEnabled);
|
||||
}
|
||||
--
|
||||
2.21.1
|
||||
|
||||
+417
@@ -0,0 +1,417 @@
|
||||
From a193c5ce59758ed5971b5bd7494f1aaf3489ed9d Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Amelkin <a.amelkin@yadro.com>
|
||||
Date: Mon, 8 Apr 2019 17:58:42 +0300
|
||||
Subject: [PATCH] Add support for boot initiator mailbox
|
||||
|
||||
Add handlers to process the chassis system option 7
|
||||
(boot initiator mailbox). The format of mailbox is
|
||||
specific to the machine/bootloader. This commit only
|
||||
adds generic handlers to process getting and setting
|
||||
of the mailbox data regardless of the content.
|
||||
|
||||
Only the IANA Enterprise number is checked in the data
|
||||
block 0. Also checked are the data boundaries.
|
||||
|
||||
It is expected that a machine-specific override for
|
||||
phosphor-settingsd sets the supported state and
|
||||
the IANA number according to the used bootloader.
|
||||
|
||||
Resolves openbmc/openbmc#3391
|
||||
|
||||
Change-Id: Iccbf74c0775f20c70e8deaa7b0a8bd995ebbffea
|
||||
Signed-off-by: Alexander Amelkin <a.amelkin@yadro.com>
|
||||
Signed-off-by: Ivan Mikhaylov <i.mikhaylov@yadro.com>
|
||||
|
||||
---
|
||||
chassishandler.cpp | 329 ++++++++++++++++++++++++++++++++++++++++++++-
|
||||
chassishandler.hpp | 1 +
|
||||
2 files changed, 326 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/chassishandler.cpp b/chassishandler.cpp
|
||||
index fb3d644..7f92b85 100644
|
||||
--- a/chassishandler.cpp
|
||||
+++ b/chassishandler.cpp
|
||||
@@ -131,6 +131,7 @@ namespace internal
|
||||
{
|
||||
|
||||
constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
|
||||
+constexpr auto bootMboxIntf = "xyz.openbmc_project.Control.Boot.Mailbox";
|
||||
constexpr auto bootTypeIntf = "xyz.openbmc_project.Control.Boot.Type";
|
||||
constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source";
|
||||
constexpr auto powerRestoreIntf =
|
||||
@@ -147,8 +148,9 @@ settings::Objects& getObjects()
|
||||
if (objectsPtr == nullptr)
|
||||
{
|
||||
objectsPtr = std::make_unique<settings::Objects>(
|
||||
- dbus, std::vector<std::string>{bootModeIntf, bootTypeIntf,
|
||||
- bootSourceIntf, powerRestoreIntf});
|
||||
+ dbus,
|
||||
+ std::vector<std::string>{bootMboxIntf, bootModeIntf, bootTypeIntf,
|
||||
+ bootSourceIntf, powerRestoreIntf});
|
||||
}
|
||||
return *objectsPtr;
|
||||
}
|
||||
@@ -1708,6 +1710,127 @@ static ipmi::Cc setBootType(ipmi::Context::ptr& ctx, const Type::Types& type)
|
||||
return ipmi::ccSuccess;
|
||||
}
|
||||
|
||||
+using MboxVec = std::vector<uint8_t>;
|
||||
+
|
||||
+// Check if Boot Mailbox is supported.
|
||||
+static std::optional<bool> isBootMboxSupported()
|
||||
+{
|
||||
+ using namespace chassis::internal;
|
||||
+ using namespace chassis::internal::cache;
|
||||
+
|
||||
+ try
|
||||
+ {
|
||||
+ settings::Objects& objects = getObjects();
|
||||
+ auto bootMbox = settings::boot::setting(objects, bootMboxIntf);
|
||||
+ const auto& bootMboxSetting = std::get<settings::Path>(bootMbox);
|
||||
+ auto method = dbus.new_method_call(
|
||||
+ objects.service(bootMboxSetting, bootMboxIntf).c_str(),
|
||||
+ bootMboxSetting.c_str(), ipmi::PROP_INTF, "Get");
|
||||
+
|
||||
+ method.append(bootMboxIntf, "Supported");
|
||||
+ auto reply = dbus.call(method);
|
||||
+ std::variant<bool> result;
|
||||
+ reply.read(result);
|
||||
+ return std::get<bool>(result);
|
||||
+ }
|
||||
+ catch (const std::exception& e)
|
||||
+ {
|
||||
+ log<level::ERR>("Error getting Boot/Mailbox/Supported",
|
||||
+ entry("ERROR=%s", e.what()));
|
||||
+ report<InternalFailure>();
|
||||
+ return std::nullopt;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static std::optional<uint24_t> getBootMboxIANA()
|
||||
+{
|
||||
+ using namespace chassis::internal;
|
||||
+ using namespace chassis::internal::cache;
|
||||
+
|
||||
+ try
|
||||
+ {
|
||||
+ settings::Objects& objects = getObjects();
|
||||
+ auto bootMbox = settings::boot::setting(objects, bootMboxIntf);
|
||||
+ const auto& bootMboxSetting = std::get<settings::Path>(bootMbox);
|
||||
+ auto method = dbus.new_method_call(
|
||||
+ objects.service(bootMboxSetting, bootMboxIntf).c_str(),
|
||||
+ bootMboxSetting.c_str(), ipmi::PROP_INTF, "Get");
|
||||
+
|
||||
+ method.append(bootMboxIntf, "IANAEnterpriseNumber");
|
||||
+ auto reply = dbus.call(method);
|
||||
+ std::variant<uint32_t> result;
|
||||
+ reply.read(result);
|
||||
+ return std::get<uint32_t>(result);
|
||||
+ }
|
||||
+ catch (const std::exception& e)
|
||||
+ {
|
||||
+ log<level::ERR>("Error getting Boot/Mailbox/IANAEnterpriseNumber",
|
||||
+ entry("ERROR=%s", e.what()));
|
||||
+ report<InternalFailure>();
|
||||
+ return std::nullopt;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static std::optional<MboxVec> getBootMbox()
|
||||
+{
|
||||
+ using namespace chassis::internal;
|
||||
+ using namespace chassis::internal::cache;
|
||||
+
|
||||
+ try
|
||||
+ {
|
||||
+ settings::Objects& objects = getObjects();
|
||||
+ auto bootMbox = settings::boot::setting(objects, bootMboxIntf);
|
||||
+ const auto& bootMboxSetting = std::get<settings::Path>(bootMbox);
|
||||
+ auto method = dbus.new_method_call(
|
||||
+ objects.service(bootMboxSetting, bootMboxIntf).c_str(),
|
||||
+ bootMboxSetting.c_str(), ipmi::PROP_INTF, "Get");
|
||||
+
|
||||
+ method.append(bootMboxIntf, "Data");
|
||||
+ auto reply = dbus.call(method);
|
||||
+ std::variant<MboxVec> result;
|
||||
+ reply.read(result);
|
||||
+ return std::get<MboxVec>(result);
|
||||
+ }
|
||||
+ catch (const std::exception& e)
|
||||
+ {
|
||||
+ log<level::ERR>("Error getting Boot/Mailbox/Data",
|
||||
+ entry("ERROR=%s", e.what()));
|
||||
+ report<InternalFailure>();
|
||||
+ return std::nullopt;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static bool setBootMbox(MboxVec data)
|
||||
+{
|
||||
+ using namespace chassis::internal;
|
||||
+ using namespace chassis::internal::cache;
|
||||
+
|
||||
+ try
|
||||
+ {
|
||||
+ settings::Objects& objects = getObjects();
|
||||
+ std::variant<MboxVec> property(data);
|
||||
+ auto bootMbox = settings::boot::setting(objects, bootMboxIntf);
|
||||
+ const auto& bootMboxSetting = std::get<settings::Path>(bootMbox);
|
||||
+ auto method = dbus.new_method_call(
|
||||
+ objects.service(bootMboxSetting, bootMboxIntf).c_str(),
|
||||
+ bootMboxSetting.c_str(), ipmi::PROP_INTF, "Set");
|
||||
+
|
||||
+ method.append(bootMboxIntf, "Data", property);
|
||||
+ dbus.call(method);
|
||||
+ return true;
|
||||
+ }
|
||||
+ catch (const std::exception& e)
|
||||
+ {
|
||||
+ log<level::ERR>("Error setting Boot/Mailbox/Data",
|
||||
+ entry("ERROR=%s", e.what()));
|
||||
+ report<InternalFailure>();
|
||||
+ return false;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static constexpr size_t normalBlockSize = 16;
|
||||
+static constexpr size_t IANAEnterpriseLength = 3;
|
||||
+
|
||||
static constexpr uint8_t setComplete = 0x0;
|
||||
static constexpr uint8_t setInProgress = 0x1;
|
||||
static uint8_t transferStatus = setComplete;
|
||||
@@ -1882,6 +2005,87 @@ ipmi::RspType<ipmi::message::Payload>
|
||||
return ipmi::responseUnspecifiedError();
|
||||
}
|
||||
}
|
||||
+ else if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
|
||||
+ BootOptionParameter::bootInitiatorMbox)
|
||||
+ {
|
||||
+ // Only allow reading the boot initiator mailbox if Mailbox is supported
|
||||
+ //
|
||||
+ // Algorithm:
|
||||
+ // 1. Get 'Supported' property from the Control.Boot.Mailbox interface
|
||||
+ // 2. If {1} is 'false', report Parameter not supported (0x80)
|
||||
+ // 3. Get Block Selector from request
|
||||
+ // 4. Get 'Data' vector from Control.Boot.Mailbox
|
||||
+ // 5. If requested block {3} exceeds total vector size {4},
|
||||
+ // report Out of space (0xC4)
|
||||
+ // 6. Return the selected block (16 bytes) from the vector
|
||||
+ try
|
||||
+ {
|
||||
+ // Check whether this option is supported
|
||||
+ std::optional<bool> isSupported = isBootMboxSupported();
|
||||
+ if (!isSupported)
|
||||
+ {
|
||||
+ return ipmi::responseUnspecifiedError();
|
||||
+ }
|
||||
+
|
||||
+ if (!*isSupported)
|
||||
+ {
|
||||
+ log<level::INFO>("Attempt to read unsupported Boot/Mailbox");
|
||||
+ return ipmi::responseParmNotSupported();
|
||||
+ }
|
||||
+
|
||||
+ // Initially assume it's block 1+
|
||||
+ std::optional<uint24_t> IANAEnterprise;
|
||||
+ size_t blockDataSize = normalBlockSize;
|
||||
+ size_t dataVecStartOffset =
|
||||
+ setSelector * normalBlockSize - IANAEnterpriseLength;
|
||||
+
|
||||
+ response.pack(bootOptionParameter, reserved1, setSelector);
|
||||
+
|
||||
+ // Adjust pointers and sizes for block 0, and fill in the IANA PEN
|
||||
+ if (0 == setSelector)
|
||||
+ {
|
||||
+ IANAEnterprise = getBootMboxIANA();
|
||||
+ if (!IANAEnterprise)
|
||||
+ {
|
||||
+ return ipmi::responseInvalidCommand();
|
||||
+ }
|
||||
+
|
||||
+ blockDataSize = normalBlockSize - IANAEnterpriseLength;
|
||||
+ dataVecStartOffset = 0;
|
||||
+
|
||||
+ response.pack(*IANAEnterprise);
|
||||
+ }
|
||||
+
|
||||
+ // Get the total data size
|
||||
+ std::optional<MboxVec> dataVec = getBootMbox();
|
||||
+ if (!dataVec)
|
||||
+ {
|
||||
+ return ipmi::responseInvalidCommand();
|
||||
+ }
|
||||
+
|
||||
+ if ((*dataVec).size() < dataVecStartOffset + blockDataSize)
|
||||
+ {
|
||||
+ size_t totalSize = (*dataVec).size() + IANAEnterpriseLength;
|
||||
+ log<level::ERR>(
|
||||
+ "Attempt to read unsupported block",
|
||||
+ entry("REQUESTED_BLOCK=%d", setSelector),
|
||||
+ entry("MAX_BLOCK=%d", totalSize / normalBlockSize));
|
||||
+ return ipmi::responseParmOutOfRange();
|
||||
+ }
|
||||
+
|
||||
+ // Copy the data to response from specified offset in d-bus vector
|
||||
+ response.append((*dataVec).data() + dataVecStartOffset,
|
||||
+ (*dataVec).data() + dataVecStartOffset +
|
||||
+ blockDataSize);
|
||||
+
|
||||
+ return ipmi::responseSuccess(std::move(response));
|
||||
+ }
|
||||
+ catch (InternalFailure& e)
|
||||
+ {
|
||||
+ report<InternalFailure>();
|
||||
+ return ipmi::responseUnspecifiedError();
|
||||
+ }
|
||||
+ }
|
||||
else
|
||||
{
|
||||
if ((bootOptionParameter >= oemParmStart) &&
|
||||
@@ -1946,9 +2150,8 @@ ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,
|
||||
return ipmi::responseSuccess();
|
||||
}
|
||||
|
||||
- /* 000101
|
||||
+ /*
|
||||
* Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
|
||||
- * This is the only parameter used by petitboot.
|
||||
*/
|
||||
|
||||
if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
|
||||
@@ -2084,6 +2287,124 @@ ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,
|
||||
return ipmi::responseUnspecifiedError();
|
||||
}
|
||||
}
|
||||
+ else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
|
||||
+ BootOptionParameter::bootInitiatorMbox)
|
||||
+ {
|
||||
+ // Only allow writing to boot initiator mailbox if:
|
||||
+ // 1. Mailbox is supported
|
||||
+ // 2. IANA PEN matches.
|
||||
+ //
|
||||
+ // Algorithm:
|
||||
+ // 1. Get 'Supported' property from Control.Boot.Mailbox interface
|
||||
+ // 2. If {1} is 'false', report Parameter not supported (0x80)
|
||||
+ // 3. Get Block Selector from request
|
||||
+ // 4. Get 'Data' array from Control.Boot.Mailbox
|
||||
+ // 5. If requested block {3} exceeds total vector size {4},
|
||||
+ // report Out of range (0xC9)
|
||||
+ // 6. If requsted block {3} is 0:
|
||||
+ // 4.1. Get IANA PEN from request
|
||||
+ // 4.2. Get 'IANAEnterpriseNumber' property from Control.Boot.Mailbox
|
||||
+ // 4.3. If {4.1} doesn't match {4.2}, report 0xCC error (Invalid
|
||||
+ // data field in request)
|
||||
+ // 7. Overwrite the 16 bytes at offset {3}*16 with the data from request
|
||||
+ // 8. Update the 'Data' array in Control.Boot.Mailbox
|
||||
+
|
||||
+ try
|
||||
+ {
|
||||
+ std::optional<bool> isSupported = isBootMboxSupported();
|
||||
+ if (!isSupported)
|
||||
+ {
|
||||
+ return ipmi::responseUnspecifiedError();
|
||||
+ }
|
||||
+
|
||||
+ if (!*isSupported)
|
||||
+ {
|
||||
+ log<level::INFO>("Attempt to read unsupported Boot/Mailbox");
|
||||
+ return ipmi::responseParmNotSupported();
|
||||
+ }
|
||||
+
|
||||
+ // Requested block
|
||||
+ uint8_t reqBlock;
|
||||
+ if (data.unpack(reqBlock) != 0)
|
||||
+ {
|
||||
+ return ipmi::responseReqDataLenInvalid();
|
||||
+ }
|
||||
+
|
||||
+ // Initially assume it's blcok 1+
|
||||
+ uint24_t reqIANAEnterprise;
|
||||
+ std::vector<uint8_t> blockData(normalBlockSize);
|
||||
+ size_t dataVecStartOffset =
|
||||
+ reqBlock * normalBlockSize - IANAEnterpriseLength;
|
||||
+
|
||||
+ // Adjust pointers and sizes for block 0, and fill in the IANA PEN
|
||||
+ if (0 == reqBlock)
|
||||
+ {
|
||||
+ if (data.unpack(reqIANAEnterprise) != 0)
|
||||
+ {
|
||||
+ return ipmi::responseReqDataLenInvalid();
|
||||
+ }
|
||||
+
|
||||
+ std::optional<uint24_t> IANAEnterprise = getBootMboxIANA();
|
||||
+ if (!IANAEnterprise)
|
||||
+ {
|
||||
+ return ipmi::responseInvalidCommand();
|
||||
+ }
|
||||
+
|
||||
+ if (*IANAEnterprise != reqIANAEnterprise)
|
||||
+ {
|
||||
+ log<level::ERR>(
|
||||
+ "Unsupported IANA Enterprise number",
|
||||
+ entry("REQUESTED_IANA=%d",
|
||||
+ static_cast<uint32_t>(reqIANAEnterprise)),
|
||||
+ entry("SUPPORTED_IANA=%d",
|
||||
+ static_cast<uint32_t>(*IANAEnterprise)));
|
||||
+ return ipmi::responseInvalidFieldRequest();
|
||||
+ }
|
||||
+
|
||||
+ // For block 0 operate on data after IANA PEN
|
||||
+ blockData.resize(normalBlockSize - IANAEnterpriseLength);
|
||||
+ dataVecStartOffset = 0;
|
||||
+ }
|
||||
+
|
||||
+ // Get the data vector from d-bus
|
||||
+ std::optional<MboxVec> dataVec = getBootMbox();
|
||||
+ if (!dataVec)
|
||||
+ {
|
||||
+ return ipmi::responseInvalidCommand();
|
||||
+ }
|
||||
+
|
||||
+ // Does the requested block exist?
|
||||
+ if ((*dataVec).size() < dataVecStartOffset + blockData.size())
|
||||
+ {
|
||||
+ size_t totalSize = (*dataVec).size() + IANAEnterpriseLength;
|
||||
+ log<level::ERR>(
|
||||
+ "Attempt to read unsupported block",
|
||||
+ entry("REQUESTED_BLOCK=%d", reqBlock),
|
||||
+ entry("MAX_BLOCK=%d", totalSize / normalBlockSize));
|
||||
+ return ipmi::responseParmOutOfRange();
|
||||
+ }
|
||||
+
|
||||
+ if (data.unpack(blockData) != 0 || !data.fullyUnpacked())
|
||||
+ {
|
||||
+ return ipmi::responseReqDataLenInvalid();
|
||||
+ }
|
||||
+
|
||||
+ // Copy the data from request to specified offset in d-bus vector
|
||||
+ for (size_t i = 0; i < blockData.size(); ++i)
|
||||
+ {
|
||||
+ (*dataVec)[dataVecStartOffset + i] = blockData[i];
|
||||
+ }
|
||||
+ if (!setBootMbox(*dataVec))
|
||||
+ {
|
||||
+ return ipmi::responseUnspecifiedError();
|
||||
+ }
|
||||
+ }
|
||||
+ catch (InternalFailure& e)
|
||||
+ {
|
||||
+ report<InternalFailure>();
|
||||
+ return ipmi::responseUnspecifiedError();
|
||||
+ }
|
||||
+ }
|
||||
else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
|
||||
BootOptionParameter::bootInfo)
|
||||
{
|
||||
diff --git a/chassishandler.hpp b/chassishandler.hpp
|
||||
index 2957127..a783bff 100644
|
||||
--- a/chassishandler.hpp
|
||||
+++ b/chassishandler.hpp
|
||||
@@ -51,6 +51,7 @@ enum class BootOptionParameter : size_t
|
||||
bootFlagValidClr = 0x3,
|
||||
bootInfo = 0x4,
|
||||
bootFlags = 0x5,
|
||||
+ bootInitiatorMbox = 0x07,
|
||||
opalNetworkSettings = 0x61
|
||||
};
|
||||
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
From 9b91e2ace1c127344e5e7d32ebb866ca636387fd Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Amelkin <alexander@amelkin.msk.ru>
|
||||
Date: Wed, 11 Jul 2018 13:16:01 +0300
|
||||
Subject: [PATCH] Fix version parsing, update AUX revision info
|
||||
|
||||
AUX Revision info was always taken from the dev_id.json
|
||||
file if it exists, overriding the value calculated from the
|
||||
active firmware version string. Also, when AUX info was
|
||||
calculated, it only properly parsed the dirtyness of the
|
||||
build.
|
||||
|
||||
With this commit the AUX info calculation will properly parse
|
||||
the git hash part and will include it as higher 3 bytes of
|
||||
the AUX info. For officially released versions the lower byte
|
||||
will be zero.
|
||||
|
||||
For development versions, bits [7:1] of the fourth byte will
|
||||
all be 1 as an indicator of non-release branch. For unofficial
|
||||
builds from release branches those bits will contain a number
|
||||
from 1 to 126 indicating a patch level since the release tag.
|
||||
|
||||
In any case the bit 0 of byte 4 is a dirtyness indicator.
|
||||
If the sources used to build the firmware were modified compared
|
||||
to the git hash, this bit will be 1.
|
||||
|
||||
WARNING: For the AUX decoding from version string to work
|
||||
properly, the dev_id.json file must NOT contain
|
||||
the `aux` property.
|
||||
|
||||
Resolves SRV-775
|
||||
End-user-impact: Version info is properly represented in the
|
||||
AUX Revision Info fields in response to the
|
||||
IPMI Get Device ID command (ipmitool mc info)
|
||||
Signed-off-by: Alexander Amelkin <a.amelkin@yadro.com>
|
||||
Signed-off-by: Alexander Filippov <a.filippov@yadro.com>
|
||||
|
||||
---
|
||||
apphandler.cpp | 226 +++++++++++++++++++++++++++++++++++--------------
|
||||
1 file changed, 163 insertions(+), 63 deletions(-)
|
||||
|
||||
diff --git a/apphandler.cpp b/apphandler.cpp
|
||||
index 6cdd78f..c62cd35 100644
|
||||
--- a/apphandler.cpp
|
||||
+++ b/apphandler.cpp
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <arpa/inet.h>
|
||||
+#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
@@ -459,33 +460,112 @@ ipmi::RspType<uint8_t, // acpiSystemPowerState
|
||||
return ipmi::responseSuccess(sysAcpiState, devAcpiState);
|
||||
}
|
||||
|
||||
+static
|
||||
+std::vector<std::string>
|
||||
+tokenize(std::string const& str,
|
||||
+ char const token[])
|
||||
+{
|
||||
+ std::vector<std::string> results;
|
||||
+ std::string::size_type j = 0;
|
||||
+ while (j < str.length())
|
||||
+ {
|
||||
+ std::string::size_type k = str.find_first_of(token, j);
|
||||
+ if (k == std::string::npos)
|
||||
+ k = str.length();
|
||||
+ results.push_back(str.substr(j, k-j));
|
||||
+ j = k + 1;
|
||||
+ }
|
||||
+ return results;
|
||||
+}
|
||||
+
|
||||
typedef struct
|
||||
{
|
||||
- char major;
|
||||
- char minor;
|
||||
- uint16_t d[2];
|
||||
+ uint8_t major;
|
||||
+ uint8_t minor;
|
||||
+ union {
|
||||
+ uint8_t aux[4]; // Individual bytes in IPMI big-endian order
|
||||
+ uint32_t aux32; // use htobe32() on writes to aux32
|
||||
+ };
|
||||
} Revision;
|
||||
|
||||
-/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
|
||||
-/* return -1 if not in those formats, this routine knows how to parse */
|
||||
+/* Currently supports the following formats. It will return -1 if not in */
|
||||
+/* those formats: */
|
||||
+/* */
|
||||
+/* Format 1: */
|
||||
/* version = v0.6-19-gf363f61-dirty */
|
||||
-/* ^ ^ ^^ ^ */
|
||||
-/* | | |----------|-- additional details */
|
||||
-/* | |---------------- Minor */
|
||||
-/* |------------------ Major */
|
||||
-/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
|
||||
-/* ^ ^ ^^ ^ */
|
||||
-/* | | |--|---------- additional details */
|
||||
-/* | |---------------- Minor */
|
||||
-/* |------------------ Major */
|
||||
-/* Additional details : If the option group exists it will force Auxiliary */
|
||||
-/* Firmware Revision Information 4th byte to 1 indicating the build was */
|
||||
-/* derived with additional edits */
|
||||
+/* ^ ^ ^^^^^^ ^^^^^ */
|
||||
+/* | | | | */
|
||||
+/* | | | `-- AUX dirty flag */
|
||||
+/* | | `---------- AUX commit hash */
|
||||
+/* | `---------------- Minor */
|
||||
+/* `------------------ Major */
|
||||
+/* */
|
||||
+/* Format 2: */
|
||||
+/* version = v1.99.10-113-g65edf7d-r3-0-g9e4f715-dirty */
|
||||
+/* ^ ^^ ^^^^^^ ^^^^^ */
|
||||
+/* | | | .-----------------' */
|
||||
+/* | | | `- AUX dirty flag */
|
||||
+/* | | `----- AUX commit hash */
|
||||
+/* | `---------------- Minor */
|
||||
+/* `------------------ Major */
|
||||
+/* */
|
||||
+/* version = v2.09-dev-794-g196400c89-some-branch-name-dirty */
|
||||
+/* ^ ^^ ^^^^^^ ^^^^^ */
|
||||
+/* | | | .-----------------------' */
|
||||
+/* | | | `- AUX dirty flag */
|
||||
+/* | | `---- AUX commit hash */
|
||||
+/* | `---------------- Minor */
|
||||
+/* `------------------ Major */
|
||||
+/* */
|
||||
+/* Format 3 (YADRO Releases): */
|
||||
+/* version = v1.0rcf2817p7-rc2-unofficial-dirty */
|
||||
+/* ^ ^ ^^^^^^ ^^ .----------^^^^^ */
|
||||
+/* | | | | `- AUX dirty flag */
|
||||
+/* | | | `------- AUX patch level (1-126), optional */
|
||||
+/* | | `-------------- AUX release number */
|
||||
+/* | `---------------- Minor */
|
||||
+/* `------------------ Major */
|
||||
+/* */
|
||||
+static
|
||||
int convertVersion(std::string s, Revision& rev)
|
||||
{
|
||||
- std::string token;
|
||||
- uint16_t commits;
|
||||
+ std::vector<std::string> tokens;
|
||||
+ bool has_release = false; // version string is of "release" format 3
|
||||
+ bool dirty = false;
|
||||
|
||||
+ constexpr int TOKEN_MAJOR = 0;
|
||||
+ constexpr int TOKEN_MINOR = 1;
|
||||
+ // These are for "release" format 3
|
||||
+ constexpr int TOKEN_MINOR_HASH = 1;
|
||||
+ constexpr int TOKEN_MINOR_PATCH = 2;
|
||||
+ // For non-release formats 1 and 2
|
||||
+ constexpr int TOKEN_HASH = 3; // Search for git hash starting from this
|
||||
+
|
||||
+ // Hash info is in the higher 24 bits of AUX F/W Revision Info
|
||||
+ constexpr int AUX_HASH_SHIFT = 8;
|
||||
+ constexpr int AUX_HASH_LEN = 6;
|
||||
+
|
||||
+ // Non-release indicator is byte 3 (bits 7..1 of AUX F/W Revision Info)
|
||||
+ constexpr int AUX_NON_REL_BYTE = 3;
|
||||
+ constexpr int AUX_NON_REL_SHIFT = 1;
|
||||
+ constexpr uint8_t AUX_NON_REL_VALUE = UINT8_MAX >> AUX_NON_REL_SHIFT;
|
||||
+
|
||||
+ // Release patch level occupies the same bits as the non-release indicator
|
||||
+ constexpr int AUX_PATCH_BYTE = AUX_NON_REL_BYTE;
|
||||
+ constexpr int AUX_PATCH_SHIFT = AUX_NON_REL_SHIFT;
|
||||
+ constexpr int AUX_MAX_PATCH = AUX_NON_REL_VALUE - 1;
|
||||
+
|
||||
+ // The least significant bit of byte 3 is the dirty flag
|
||||
+ constexpr int AUX_DIRTY_BYTE = 3;
|
||||
+ constexpr int AUX_DIRTY_SHIFT = 0;
|
||||
+
|
||||
+ // Use base-16 to convert decimals to BCD
|
||||
+ constexpr int BCD_BASE = 16;
|
||||
+
|
||||
+ // First of all clear the revision
|
||||
+ rev = {0};
|
||||
+
|
||||
+ // Cut off the optional 'v' at the beginning
|
||||
auto location = s.find_first_of('v');
|
||||
if (location != std::string::npos)
|
||||
{
|
||||
@@ -494,64 +574,77 @@ int convertVersion(std::string s, Revision& rev)
|
||||
|
||||
if (!s.empty())
|
||||
{
|
||||
- location = s.find_first_of(".");
|
||||
- if (location != std::string::npos)
|
||||
+ int hash = 0;
|
||||
+
|
||||
+ if (s.find("dirty") != std::string::npos)
|
||||
{
|
||||
- rev.major =
|
||||
- static_cast<char>(std::stoi(s.substr(0, location), 0, 10));
|
||||
- token = s.substr(location + 1);
|
||||
+ dirty = true;
|
||||
}
|
||||
|
||||
- if (!token.empty())
|
||||
+ tokens = tokenize(s, ".-");
|
||||
+
|
||||
+ if (!tokens.empty())
|
||||
{
|
||||
- location = token.find_first_of(".-");
|
||||
- if (location != std::string::npos)
|
||||
+ rev.major = std::stoi(tokens[TOKEN_MAJOR], 0, BCD_BASE);
|
||||
+ }
|
||||
+
|
||||
+ if (tokens.size() > TOKEN_MINOR)
|
||||
+ {
|
||||
+ rev.minor = std::stoi(tokens[TOKEN_MINOR], 0, BCD_BASE);
|
||||
+
|
||||
+ // Minor version token may also contain release/patchlevel info
|
||||
+ std::vector<std::string> minortok;
|
||||
+
|
||||
+ minortok = tokenize(tokens[TOKEN_MINOR], "rp");
|
||||
+
|
||||
+ if (minortok.size() > TOKEN_MINOR_HASH)
|
||||
{
|
||||
- rev.minor = static_cast<char>(
|
||||
- std::stoi(token.substr(0, location), 0, 10));
|
||||
- token = token.substr(location + 1);
|
||||
+ // hash is plain hex
|
||||
+ hash= std::stoi(minortok[TOKEN_MINOR_HASH], 0, 16);
|
||||
+ has_release = true;
|
||||
+ }
|
||||
+
|
||||
+ if (minortok.size() > TOKEN_MINOR_PATCH)
|
||||
+ {
|
||||
+ // Patch level is encoded as binary, not BCD.
|
||||
+ // That is to allow for a wider range.
|
||||
+ int pl = std::stoi(minortok[TOKEN_MINOR_PATCH], 0, 10);
|
||||
+ uint8_t patchlevel = (pl > AUX_MAX_PATCH)
|
||||
+ ? AUX_MAX_PATCH
|
||||
+ : pl;
|
||||
+ rev.aux[AUX_PATCH_BYTE] = patchlevel << AUX_PATCH_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
- // Capture the number of commits on top of the minor tag.
|
||||
- // I'm using BE format like the ipmi spec asked for
|
||||
- location = token.find_first_of(".-");
|
||||
- if (!token.empty())
|
||||
+ // If it's not a "release" format 3, then search for
|
||||
+ // letter 'g' indicating the position of a git hash
|
||||
+ // in the version string
|
||||
+ if (!has_release && tokens.size() > TOKEN_HASH)
|
||||
{
|
||||
- commits = std::stoi(token.substr(0, location), 0, 16);
|
||||
- rev.d[0] = (commits >> 8) | (commits << 8);
|
||||
-
|
||||
- // commit number we skip
|
||||
- location = token.find_first_of(".-");
|
||||
- if (location != std::string::npos)
|
||||
+ std::string hashstr;
|
||||
+ for (size_t i = TOKEN_HASH; i < tokens.size(); ++i)
|
||||
{
|
||||
- token = token.substr(location + 1);
|
||||
+ // Find the first token that looks like a git hash.
|
||||
+ // We think here that anything starting with a 'g' is a match.
|
||||
+ if ('g' == tokens[i][0])
|
||||
+ {
|
||||
+ // Cut off the 'g', take only the first AUX_HASH_LEN digits
|
||||
+ hashstr = tokens[i].substr(1, AUX_HASH_LEN);
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- rev.d[0] = 0;
|
||||
- }
|
||||
|
||||
- if (location != std::string::npos)
|
||||
- {
|
||||
- token = token.substr(location + 1);
|
||||
+ // Hash is plain hex
|
||||
+ hash = std::stoi(hashstr, 0, 16);
|
||||
+ rev.aux[AUX_NON_REL_BYTE] |= AUX_NON_REL_VALUE << AUX_NON_REL_SHIFT;
|
||||
}
|
||||
+ rev.aux32 |= htobe32(hash << AUX_HASH_SHIFT);
|
||||
+ rev.aux[AUX_DIRTY_BYTE] |= dirty << AUX_DIRTY_SHIFT;
|
||||
|
||||
- // Any value of the optional parameter forces it to 1
|
||||
- location = token.find_first_of(".-");
|
||||
- if (location != std::string::npos)
|
||||
- {
|
||||
- token = token.substr(location + 1);
|
||||
- }
|
||||
- commits = (!token.empty()) ? 1 : 0;
|
||||
-
|
||||
- // We do this operation to get this displayed in least significant bytes
|
||||
- // of ipmitool device id command.
|
||||
- rev.d[1] = (commits >> 8) | (commits << 8);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
- return 0;
|
||||
+ return -1;
|
||||
}
|
||||
|
||||
/* @brief: Implement the Get Device ID IPMI command per the IPMI spec
|
||||
@@ -623,7 +716,7 @@ ipmi::RspType<uint8_t, // Device ID
|
||||
|
||||
rev.minor = (rev.minor > 99 ? 99 : rev.minor);
|
||||
devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
|
||||
- std::memcpy(&devId.aux, rev.d, 4);
|
||||
+ std::memcpy(&devId.aux, rev.aux, 4);
|
||||
haveBMCVersion = true;
|
||||
}
|
||||
}
|
||||
@@ -643,7 +736,14 @@ ipmi::RspType<uint8_t, // Device ID
|
||||
devId.addnDevSupport = data.value("addn_dev_support", 0);
|
||||
devId.manufId = data.value("manuf_id", 0);
|
||||
devId.prodId = data.value("prod_id", 0);
|
||||
- devId.aux = data.value("aux", 0);
|
||||
+
|
||||
+ // Use the AUX data from the file only for overriding
|
||||
+ // the data obtained from version string.
|
||||
+ if (data.contains("aux"))
|
||||
+ {
|
||||
+ // AUX F/W Revision Info is MSB first (big-endian)
|
||||
+ devId.aux = htobe32(data.value("aux", 0));
|
||||
+ }
|
||||
|
||||
// Set the availablitity of the BMC.
|
||||
defaultActivationSetting = data.value("availability", true);
|
||||
@@ -0,0 +1,7 @@
|
||||
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
|
||||
|
||||
SRC_URI += "\
|
||||
file://0001-Add-support-for-persistent-only-settings.patch \
|
||||
file://0002-Add-support-for-boot-initiator-mailbox.patch \
|
||||
file://0003-Fix-version-parsing-update-AUX-revision-info.patch \
|
||||
"
|
||||
Reference in New Issue
Block a user