diff options
Diffstat (limited to 'meta-yadro/recipes-phosphor/ipmi')
6 files changed, 861 insertions, 0 deletions
diff --git a/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend new file mode 100644 index 000000000..72d991c7e --- /dev/null +++ b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-config/cipher_list.json b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-config/cipher_list.json new file mode 100644 index 000000000..767fc708c --- /dev/null +++ b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-config/cipher_list.json @@ -0,0 +1,14 @@ +{ + "a":{ + "cipher":3, + "authentication":1, + "integrity":1, + "confidentiality":1 + }, + "b":{ + "cipher":17, + "authentication":3, + "integrity":4, + "confidentiality":1 + } +} diff --git a/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host/0001-Add-support-for-persistent-only-settings.patch b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host/0001-Add-support-for-persistent-only-settings.patch new file mode 100644 index 000000000..f1e2c4ae4 --- /dev/null +++ b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host/0001-Add-support-for-persistent-only-settings.patch @@ -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 + diff --git a/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host/0002-Add-support-for-boot-initiator-mailbox.patch b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host/0002-Add-support-for-boot-initiator-mailbox.patch new file mode 100644 index 000000000..dd7610975 --- /dev/null +++ b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host/0002-Add-support-for-boot-initiator-mailbox.patch @@ -0,0 +1,418 @@ +From ec6765e9aa35871f8d02cb0b5b47d96be18f4804 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 | 328 ++++++++++++++++++++++++++++++++++++++++++++- + chassishandler.hpp | 1 + + 2 files changed, 325 insertions(+), 4 deletions(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 3250b2c..48cda21 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -136,6 +136,7 @@ namespace internal + { + + constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode"; ++constexpr auto bootMboxIntf = "xyz.openbmc_project.Control.Boot.Mailbox"; + constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source"; + constexpr auto powerRestoreIntf = + "xyz.openbmc_project.Control.Power.RestorePolicy"; +@@ -151,8 +152,8 @@ settings::Objects& getObjects() + if (objectsPtr == nullptr) + { + objectsPtr = std::make_unique<settings::Objects>( +- dbus, std::vector<std::string>{bootModeIntf, bootSourceIntf, +- powerRestoreIntf}); ++ dbus, std::vector<std::string>{bootMboxIntf, bootModeIntf, ++ bootSourceIntf, powerRestoreIntf}); + } + return *objectsPtr; + } +@@ -1670,6 +1671,127 @@ static ipmi::Cc setBootMode(const Mode::Modes& mode) + 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; ++ + /** @brief implements the Get Chassis system boot option + * @param bootOptionParameter - boot option parameter selector + * @param reserved1 - reserved bit +@@ -1783,6 +1905,87 @@ ipmi::RspType<ipmi::message::Payload> + return ipmi::responseUnspecifiedError(); + } + } ++ else if (static_cast<uint8_t>(bootOptionParameter) == ++ static_cast<uint8_t>(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) && +@@ -1825,9 +2028,8 @@ ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx, + using namespace boot_options; + ipmi::Cc rc; + +- /* 000101 ++ /* + * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. +- * This is the only parameter used by petitboot. + */ + + if (parameterSelector == +@@ -1954,6 +2156,124 @@ ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx, + return ipmi::responseUnspecifiedError(); + } + } ++ else if (parameterSelector == ++ static_cast<uint7_t>(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 (parameterSelector == + static_cast<uint7_t>(BootOptionParameter::bootInfo)) + { +diff --git a/chassishandler.hpp b/chassishandler.hpp +index 93de2c0..33ad25f 100644 +--- a/chassishandler.hpp ++++ b/chassishandler.hpp +@@ -48,6 +48,7 @@ enum class BootOptionParameter : size_t + { + bootInfo = 0x4, + bootFlags = 0x5, ++ bootInitiatorMbox = 0x07, + opalNetworkSettings = 0x61 + }; + +-- +2.26.2 + diff --git a/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host/0003-Fix-version-parsing-update-AUX-revision-info.patch b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host/0003-Fix-version-parsing-update-AUX-revision-info.patch new file mode 100644 index 000000000..695b16c69 --- /dev/null +++ b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host/0003-Fix-version-parsing-update-AUX-revision-info.patch @@ -0,0 +1,329 @@ +From 605210dca3d6803839a8bcea4b5b83866c01f2da 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.filippov@yadro.com> + +--- + apphandler.cpp | 228 +++++++++++++++++++++++++++++++++++-------------- + 1 file changed, 165 insertions(+), 63 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index 90818a9..93e3b6c 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 +576,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, 16)); +- 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, 16)); +- 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 +718,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); + } + + // IPMI Spec version 2.0 +@@ -640,7 +735,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); +-- +2.28.0 + diff --git a/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend new file mode 100644 index 000000000..b4b1a2f1e --- /dev/null +++ b/meta-yadro/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend @@ -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 \ +" |