diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor')
221 files changed, 25713 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/beepcode-mgr.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/beepcode-mgr.bb new file mode 100644 index 000000000..f24f69a5f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/beepcode-mgr.bb @@ -0,0 +1,23 @@ + +SUMMARY = "Beep code manager service" +DESCRIPTION = "The beep code manager service will provide a method for beep code" + +SRC_URI = "\ + file://CMakeLists.txt;subdir=${BP} \ + file://beepcode_mgr.cpp;subdir=${BP} \ + " +PV = "0.1" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SYSTEMD_SERVICE:${PN} = "beepcode-mgr.service" + +inherit cmake +inherit obmc-phosphor-systemd + +DEPENDS += " \ + sdbusplus \ + phosphor-logging \ + boost \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/.clang-format b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/.clang-format new file mode 100644 index 000000000..dd2770837 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/.clang-format @@ -0,0 +1,98 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +PointerAlignment: Left +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^[<"](gtest|gmock)' + Priority: 5 + - Regex: '^"config.h"' + Priority: -1 + - Regex: '^".*\.hpp"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/CMakeLists.txt new file mode 100644 index 000000000..472257279 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required (VERSION 3.5 FATAL_ERROR) +project (beepcode-mgr CXX) +set (CMAKE_CXX_STANDARD 17) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti") + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +# boost support +find_package (Boost REQUIRED) +# pkg_check_modules(Boost boost REQUIRED) +include_directories (${Boost_INCLUDE_DIRS}) +add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY) +add_definitions (-DBOOST_SYSTEM_NO_DEPRECATED) +add_definitions (-DBOOST_ALL_NO_LIB) +add_definitions (-DBOOST_NO_RTTI) +add_definitions (-DBOOST_NO_TYPEID) +add_definitions (-DBOOST_ASIO_DISABLE_THREADS) + +# import sdbusplus +find_package (PkgConfig REQUIRED) +pkg_check_modules (SDBUSPLUSPLUS sdbusplus REQUIRED) +include_directories (${SDBUSPLUSPLUS_INCLUDE_DIRS}) +link_directories (${SDBUSPLUSPLUS_LIBRARY_DIRS}) + +# import phosphor-logging +find_package (PkgConfig REQUIRED) +pkg_check_modules (LOGGING phosphor-logging REQUIRED) +include_directories (${LOGGING_INCLUDE_DIRS}) +link_directories (${LOGGING_LIBRARY_DIRS}) + +add_executable (beepcode-mgr beepcode_mgr.cpp) + +target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES}) +target_link_libraries (${PROJECT_NAME} ${SDBUSPLUSPLUS_LIBRARIES} + phosphor_logging) + +install (TARGETS beepcode-mgr DESTINATION bin) diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/beepcode-mgr.service b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/beepcode-mgr.service new file mode 100644 index 000000000..8099e2541 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/beepcode-mgr.service @@ -0,0 +1,12 @@ +[Unit] +Description=Beep code manager + +[Service] +Restart=always +RestartSec=2 +ExecStart=/usr/bin/beepcode-mgr +StartLimitInterval=0 +Type=simple + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/beepcode_mgr.cpp b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/beepcode_mgr.cpp new file mode 100644 index 000000000..5a2deceaf --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/beepcode_mgr.cpp @@ -0,0 +1,321 @@ +/* Copyright 2019 Intel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <linux/input.h> + +#include <boost/asio/io_service.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <chrono> +#include <iostream> +#include <phosphor-logging/log.hpp> +#include <sdbusplus/asio/object_server.hpp> + +static constexpr uint32_t defaultBeepFrequence = 2000; +static constexpr uint32_t defaultBeepDurationMs = 300; +// Duration between two beeps +static constexpr uint32_t defaultInterBeepDurationMs = 300; +// Duration between two 4-bit digitals +static constexpr uint32_t defaultInterDigitBeepDurationMs = 800; +// Duration between two patterns +static constexpr uint32_t defaultPostBeepDurationMs = 1000; + +static constexpr uint8_t offBeepState = 0; +static constexpr uint8_t onBeepState = 1; +// finish 1 bit beep +static constexpr uint8_t interBeepState = 2; +// finish 4 bits beep +static constexpr uint8_t interDigitBeepState = 3; +// finish all bits beep +static constexpr uint8_t postBeepState = 4; + +static const std::vector<uint32_t> beepDelayTable = { + 0, defaultBeepDurationMs, defaultInterBeepDurationMs, + defaultInterDigitBeepDurationMs, defaultPostBeepDurationMs}; + +static constexpr uint32_t bpBitCount = 4; +static constexpr uint32_t bpShiftCount = 32; +static constexpr uint32_t bpMask = 0xf0000000; + +// beep code priority +static constexpr uint8_t beepOff = 0; +static constexpr uint8_t beepVRWatchdogTimeout = 1; +static constexpr uint8_t beepPSUFailure = 2; +static constexpr uint8_t beepCPUMIssing = 3; +static constexpr uint8_t beepCPUCatError = 4; +static constexpr uint8_t beepCPUErr2 = 5; +static constexpr uint8_t beepVoltageMismatch = 6; +static constexpr uint8_t beepCPUConfigError = 7; +static constexpr uint8_t beepPowerFail = 8; +static constexpr uint8_t beepPowerGoodTimeOut = 9; +static constexpr uint8_t beepMax = 10; + +// priority, abbrev name map +static const std::map<uint8_t, std::string> beepCodeNameList = { + {beepVRWatchdogTimeout, "VRWatchdogTimeout"}, + {beepPSUFailure, "PSUFailure"}, + {beepCPUMIssing, "CPUMissing"}, + {beepCPUCatError, "CPUCatError"}, + {beepCPUErr2, "CPUErr2"}, + {beepVoltageMismatch, "VoltageMismatch"}, + {beepCPUConfigError, "CPUConfigError"}, + {beepPowerFail, "PowerFail"}, + {beepPowerGoodTimeOut, "PowerGoodTimeOut"}, +}; + +// priority, code pattern map +static const std::map<uint8_t, std::string> beepCodePatternList = { + {beepVRWatchdogTimeout, "1-5-1-2"}, {beepPSUFailure, "1-5-1-4"}, + {beepCPUMIssing, "1-5-2-1"}, {beepCPUCatError, "1-5-2-2"}, + {beepCPUErr2, "1-5-2-3"}, {beepVoltageMismatch, "1-5-2-4"}, + {beepCPUConfigError, "1-5-2-5"}, {beepPowerFail, "1-5-4-2"}, + {beepPowerGoodTimeOut, "1-5-4-4"}, +}; + +static const std::vector<uint32_t> beepCodeTable = { + 0, 0x1512, 0x1514, 0x1521, 0x1522, 0x1523, 0x1524, 0x1525, 0x1542, 0x1544}; + +static constexpr char bpDevName[] = "/dev/input/event0"; +static constexpr char bpBusName[] = "xyz.openbmc_project.BeepCode"; +static constexpr char bpObjName[] = "/xyz/openbmc_project/BeepCode"; +static constexpr char bpIntfName[] = "xyz.openbmc_project.BeepCode"; +static constexpr char bpMethodName[] = "Beep"; + +static std::shared_ptr<sdbusplus::asio::dbus_interface> bpIface; +static boost::asio::io_service io; +static auto conn = std::make_shared<sdbusplus::asio::connection>(io); + +class Beeper +{ + public: + Beeper(boost::asio::io_service& io) + { + timer = std::make_unique<boost::asio::steady_timer>(io); + fdBeepDev = -1; + currentCount = 0; + currentBeepCode = 0; + currentMask = bpMask; + currentShift = bpShiftCount; + currentState = offBeepState; + timerRunning = false; + } + + ~Beeper() + { + } + + void beep(const uint8_t& beepPriority) + { + if (timerRunning) + { + pendingList.push_back(beepPriority); + pendingList.sort(std::greater<uint8_t>()); + return; + } + + performBeep(beepPriority); + } + + private: + void performBeep(const uint8_t& beepPriority) + { + currentBeepCode = beepCodeTable[beepPriority]; + currentCount = 0; + currentMask = bpMask; + currentShift = bpShiftCount; + getCurrentCount(); + startBeep(defaultBeepFrequence); + currentState = onBeepState; + currentCount--; + timerRunning = true; + startBeepTimer(); + } + + void startBeepTimer() + { + timer->expires_after( + std::chrono::milliseconds(beepDelayTable[currentState])); + timer->async_wait([this](const boost::system::error_code& ec) { + // timer timeout + switch (currentState) + { + case onBeepState: + stopBeep(); + if (currentCount == 0) + { + // finished the current 4-bit + if (currentBeepCode == 0) + { + // finished all bits + currentState = postBeepState; + } + else + { + // start next 4-bit + currentState = interDigitBeepState; + getCurrentCount(); + currentCount--; + } + } + else + { + // still in 4-bit processing + currentCount--; + currentState = interBeepState; + } + startBeepTimer(); + break; + + case interBeepState: + case interDigitBeepState: + startBeep(defaultBeepFrequence); + currentState = onBeepState; + startBeepTimer(); + break; + case postBeepState: + if (pendingList.size() != 0) + { + // continue the next new beepcode + uint8_t beepPriority = pendingList.front(); + pendingList.pop_front(); + performBeep(beepPriority); + } + else + { + timerRunning = false; + } + break; + + default: + std::cerr << "Incorrect beepState: " + << static_cast<unsigned int>(currentState) + << std::endl; + break; + } + }); + } + + void startBeep(uint32_t freq) + { + if (fdBeepDev != -1) + { + std::cerr << "beep device is opening already!" << std::endl; + ::close(fdBeepDev); + fdBeepDev = -1; + } + + if ((fdBeepDev = ::open(bpDevName, O_RDWR | O_CLOEXEC)) < 0) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Failed to open input device"); + return; + } + + struct input_event event; + event.type = EV_SND; + event.code = SND_TONE; + event.value = freq; + + if (::write(fdBeepDev, &event, sizeof(struct input_event)) != + sizeof(struct input_event)) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Failed to write a tone sound event"); + ::close(fdBeepDev); + fdBeepDev = -1; + return; + } + return; + } + + void stopBeep() + { + if (fdBeepDev == -1) + { + std::cerr << "beep device is closed!" << std::endl; + return; + } + + ::close(fdBeepDev); + fdBeepDev = -1; + } + + // Split the beep code based on bpBitCount, for example 0x1544, + // currentCount=1, 5, 4, 4 + void getCurrentCount() + { + while (currentCount == 0) + { + currentCount = currentMask & currentBeepCode; + currentShift -= bpBitCount; + currentCount >>= currentShift; + currentBeepCode = currentBeepCode & ~currentMask; + currentMask >>= bpBitCount; + if (currentMask == 0) + { + break; + } + } + } + + int fdBeepDev; + bool timerRunning; + uint32_t currentCount; + uint32_t currentBeepCode; + uint32_t currentMask; + uint32_t currentShift; + uint8_t currentState; + std::unique_ptr<boost::asio::steady_timer> timer; + std::list<uint8_t> pendingList; +}; + +static Beeper beeper(io); + +// dbus method +static void beep(const uint8_t& beepPriority) +{ + if ((beepPriority >= beepMax) || (beepPriority == beepOff)) + { + std::cerr << "Incorrect input: " + << static_cast<unsigned int>(beepPriority) << std::endl; + return; + } + + beeper.beep(beepPriority); + + return; +} + +int main(int argc, char** argv) +{ + phosphor::logging::log<phosphor::logging::level::INFO>( + "Starting BeepCode service"); + + conn->request_name(bpBusName); + sdbusplus::asio::object_server server = + sdbusplus::asio::object_server(conn); + bpIface = server.add_interface(bpObjName, bpIntfName); + + bpIface->register_property("BeepCodeNameList", beepCodeNameList, + sdbusplus::asio::PropertyPermission::readOnly); + bpIface->register_property("BeepCodePatternList", beepCodePatternList, + sdbusplus::asio::PropertyPermission::readOnly); + bpIface->register_method(bpMethodName, beep); + bpIface->initialize(); + + io.run(); + return 0; +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/cmake-format.json b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/cmake-format.json new file mode 100644 index 000000000..583c255a3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/files/cmake-format.json @@ -0,0 +1,12 @@ +{ + "enum_char": ".", + "line_ending": "unix", + "bullet_char": "*", + "max_subargs_per_line": 99, + "command_case": "lower", + "tab_size": 4, + "line_width": 80, + "separate_fn_name_with_space": true, + "dangle_parens": true, + "separate_ctrl_name_with_space": true +}
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/biosconfig-manager/biosconfig-manager_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/biosconfig-manager/biosconfig-manager_git.bb new file mode 100644 index 000000000..711e4eced --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/biosconfig-manager/biosconfig-manager_git.bb @@ -0,0 +1,28 @@ +SUMMARY = "BIOS Config Manager daemon for managing the BIOS configuration" +DESCRIPTION = "To view and modify BIOS setup configuration remotely via BMC" + +PV = "1.0+git${SRCPV}" + +S = "${WORKDIR}/git" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" +inherit meson systemd + +SRC_URI = "git://github.com/openbmc/bios-settings-mgr.git" +SRCREV = "29656f07b7e81c0bb13ca119b4c6ef62f5e79a18" + +SYSTEMD_SERVICE:${PN} += " \ + xyz.openbmc_project.biosconfig_manager.service \ + xyz.openbmc_project.biosconfig_password.service \ + " + +DEPENDS += " \ + systemd \ + sdbusplus \ + libgpiod \ + phosphor-logging \ + boost \ + nlohmann-json \ + libtinyxml2 \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0001-Improve-initialization-of-I2C-sensors.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0001-Improve-initialization-of-I2C-sensors.patch new file mode 100644 index 000000000..e47111c56 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0001-Improve-initialization-of-I2C-sensors.patch @@ -0,0 +1,493 @@ +From a85d4c9cf702965593ec771e57a975e30d1d5853 Mon Sep 17 00:00:00 2001 +From: Johnathan Mantey <johnathanx.mantey@intel.com> +Date: Tue, 13 Oct 2020 15:00:51 -0700 +Subject: [PATCH] Improve initialization of I2C sensors + +After an AC cycle validation has witnessed some systems sensors are +missing. As Entity Manager begins the process of scanning for +sesnsors, and creating the hardware monitoring nodes, there are +occassionally some failures to correctly create the node. This +manifests itself by the 'dd' kernel driver emitting an -EBUSY error +message. Unfortunately the 'dd' driver also eats the error code, and +continues. This is by design. + +This commit modifies how the nodes are initialized. The steps taken: +1. Determine if the node is already present +2. Create the node if it is not +3. Set a timer, to give the kernel time to create the node +4. For those sensors that create a "hwmon" subdir, search for that +directory after the timer elapses. +5. If the subdir is not present, delete the device, and try again by +initiating another timer. +6. Continue until the subdir exists, or a retry count expires. + +Tested: +Ran AC cycles via a script. +After each cycle, wait for the SUT to DC on, and arrive at the EFI +Shell> propmt. +Issue "ipmitool sensor list", capturing the results +Search the list for all of the sensors that have been reported as +missing after AC cycles. + +Change-Id: I118df674162677d66e7d211b089430fce384086b +Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com> +--- + include/devices.hpp | 167 +++++++++++++++++++++----------------- + src/Overlay.cpp | 192 ++++++++++++++++++++++++++++++++++---------- + 2 files changed, 243 insertions(+), 116 deletions(-) + +diff --git a/include/devices.hpp b/include/devices.hpp +index 50fbe63..2e299a0 100644 +--- a/include/devices.hpp ++++ b/include/devices.hpp +@@ -31,107 +31,130 @@ struct CmpStr + + struct ExportTemplate + { +- ExportTemplate(const char* params, const char* dev) : +- parameters(params), device(dev){}; ++ ExportTemplate(const char* params, const char* dev, const char* constructor, ++ const char* destructor, bool createsHWMon) : ++ parameters(params), ++ devicePath(dev), add(constructor), remove(destructor), ++ createsHWMon(createsHWMon){}; + const char* parameters; +- const char* device; ++ const char* devicePath; ++ const char* add; ++ const char* remove; ++ bool createsHWMon; + }; + + const boost::container::flat_map<const char*, ExportTemplate, CmpStr> + exportTemplates{ + {{"EEPROM_24C02", + ExportTemplate("24c02 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ "/sys/bus/i2c/devices/i2c-$Bus/new_device", ++ "new_device", "delete_device", false)}, + {"EEPROM_24C64", + ExportTemplate("24c64 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"ADM1266", +- ExportTemplate("adm1266 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ "/sys/bus/i2c/devices/i2c-$Bus/new_device", ++ "new_device", "delete_device", false)}, ++ {"24C02", ++ ExportTemplate("24c02 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", false)}, ++ {"24C64", ++ ExportTemplate("24c64 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", false)}, ++ {"ADM1266", ExportTemplate("adm1266 $Address", ++ "/sys/bus/i2c/devices/i2c-$Bus/new_device", ++ "new_device", "delete_device", false)}, + {"ADM1272", +- ExportTemplate("adm1272 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"EEPROM", ExportTemplate("eeprom $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("adm1272 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", false)}, ++ {"EEPROM", ++ ExportTemplate("eeprom $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", false)}, + {"EMC1412", +- ExportTemplate("emc1412 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("emc1412 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"EMC1413", +- ExportTemplate("emc1413 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("emc1413 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"EMC1414", +- ExportTemplate("emc1414 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"Gpio", ExportTemplate("$Index", "/sys/class/gpio/export")}, +- {"INA230", ExportTemplate("ina230 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("emc1414 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, ++ {"Gpio", ExportTemplate("$Index", "/sys/class/gpio", "export", ++ "unexport", false)}, ++ {"INA230", ++ ExportTemplate("ina230 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"ISL68137", +- ExportTemplate("isl68137 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("isl68137 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"ISL68223", +- ExportTemplate("isl68223 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("isl68223 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"ISL69243", +- ExportTemplate("isl69243 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("isl69243 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"MAX16601", +- ExportTemplate("max16601 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("max16601 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"MAX20710", +- ExportTemplate("max20710 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("max20710 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"MAX20730", +- ExportTemplate("max20730 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("max20730 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"MAX20734", +- ExportTemplate("max20734 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("max20734 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"MAX20796", +- ExportTemplate("max20796 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("max20796 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"MAX31725", +- ExportTemplate("max31725 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("max31725 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"MAX31730", +- ExportTemplate("max31730 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"MAX34440", +- ExportTemplate("max34440 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("max31730 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, ++ {"MAX34440", ExportTemplate("max34440 $Address", ++ "/sys/bus/i2c/devices/i2c-$Bus/new_device", ++ "new_device", "delete_device", true)}, + {"MAX34451", +- ExportTemplate("max34451 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("max34451 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"MAX6654", +- ExportTemplate("max6654 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("max6654 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"PCA9543Mux", +- ExportTemplate("pca9543 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("pca9543 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", false)}, + {"PCA9544Mux", +- ExportTemplate("pca9544 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("pca9544 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", false)}, + {"PCA9545Mux", +- ExportTemplate("pca9545 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("pca9545 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", false)}, + {"PCA9546Mux", +- ExportTemplate("pca9546 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("pca9546 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", false)}, + {"PCA9547Mux", +- ExportTemplate("pca9547 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"SBTSI", ExportTemplate("sbtsi $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"pmbus", ExportTemplate("pmbus $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"TMP112", ExportTemplate("tmp112 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"TMP175", ExportTemplate("tmp175 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"TMP421", ExportTemplate("tmp421 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, +- {"TMP441", ExportTemplate("tmp441 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}, ++ ExportTemplate("pca9547 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", false)}, ++ {"SBTSI", ++ ExportTemplate("sbtsi $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, ++ {"pmbus", ++ ExportTemplate("pmbus $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, ++ {"TMP112", ++ ExportTemplate("tmp112 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, ++ {"TMP175", ++ ExportTemplate("tmp175 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, ++ {"TMP421", ++ ExportTemplate("tmp421 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, ++ {"TMP441", ++ ExportTemplate("tmp441 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}, + {"TMP75", +- ExportTemplate("tmp75 $Address", +- "/sys/bus/i2c/devices/i2c-$Bus/new_device")}}}; ++ ExportTemplate("tmp75 $Address", "/sys/bus/i2c/devices/i2c-$Bus", ++ "new_device", "delete_device", true)}}}; + } // namespace devices +diff --git a/src/Overlay.cpp b/src/Overlay.cpp +index cb6ed10..7a3089e 100644 +--- a/src/Overlay.cpp ++++ b/src/Overlay.cpp +@@ -21,6 +21,8 @@ + #include "devices.hpp" + + #include <boost/algorithm/string/predicate.hpp> ++#include <boost/asio/io_context.hpp> ++#include <boost/asio/steady_timer.hpp> + #include <boost/container/flat_map.hpp> + #include <boost/container/flat_set.hpp> + #include <boost/process/child.hpp> +@@ -32,6 +34,8 @@ + #include <regex> + #include <string> + ++extern boost::asio::io_context io; ++ + constexpr const char* OUTPUT_DIR = "/tmp/overlays"; + constexpr const char* TEMPLATE_CHAR = "$"; + constexpr const char* HEX_FORMAT_STR = "0x"; +@@ -113,16 +117,149 @@ void linkMux(const std::string& muxName, size_t busIndex, size_t address, + } + } + ++static int deleteDevice(const std::string& devicePath, ++ std::shared_ptr<uint64_t> address, ++ const std::string& destructor) ++{ ++ if (!address) ++ { ++ return -1; ++ } ++ std::filesystem::path deviceDestructor(devicePath); ++ deviceDestructor /= destructor; ++ std::ofstream deviceFile(deviceDestructor); ++ if (!deviceFile.good()) ++ { ++ std::cerr << "Error writing " << deviceDestructor << "\n"; ++ return -1; ++ } ++ deviceFile << std::to_string(*address); ++ deviceFile.close(); ++ return 0; ++} ++ ++static int createDevice(const std::string& devicePath, ++ const std::string& parameters, ++ const std::string& constructor) ++{ ++ std::filesystem::path deviceConstructor(devicePath); ++ deviceConstructor /= constructor; ++ std::ofstream deviceFile(deviceConstructor); ++ if (!deviceFile.good()) ++ { ++ std::cerr << "Error writing " << deviceConstructor << "\n"; ++ return -1; ++ } ++ deviceFile << parameters; ++ deviceFile.close(); ++ ++ return 0; ++} ++ ++static bool deviceIsCreated(const std::string& devicePath, ++ std::shared_ptr<uint64_t> bus, ++ std::shared_ptr<uint64_t> address, ++ const bool retrying) ++{ ++ // Prevent the device from being created a second time. ++ if (bus && address) ++ { ++ std::ostringstream hex; ++ hex << std::hex << *address; ++ std::string addressHex = hex.str(); ++ std::string busStr = std::to_string(*bus); ++ ++ if (std::filesystem::is_directory(devicePath)) ++ { ++ for (const auto& path : ++ std::filesystem::directory_iterator(devicePath)) ++ { ++ if (!std::filesystem::is_directory(path)) ++ { ++ continue; ++ } ++ ++ const std::string& directoryName = path.path().filename(); ++ if (boost::starts_with(directoryName, busStr) && ++ boost::ends_with(directoryName, addressHex)) ++ { ++ if (retrying) ++ { ++ // subsequent attempts should find the hwmon subdir. ++ std::filesystem::path hwmonDir(devicePath); ++ hwmonDir /= directoryName; ++ hwmonDir /= "hwmon"; ++ bool dirFound = ++ (std::filesystem::is_directory(hwmonDir)); ++ return dirFound; ++ } ++ else ++ { ++ return true; ++ } ++ } ++ } ++ return false; ++ } ++ } ++ return false; ++} ++ ++constexpr size_t totalBuildDeviceRetries = 5; ++static int buildDevice(const std::string& devicePath, ++ const std::string& parameters, ++ std::shared_ptr<uint64_t> bus, ++ std::shared_ptr<uint64_t> address, ++ const std::string& constructor, ++ const std::string& destructor, const bool createsHWMon, ++ const size_t retries = totalBuildDeviceRetries) ++{ ++ bool tryAgain = false; ++ if (!retries) ++ { ++ return -1; ++ } ++ ++ if (!deviceIsCreated(devicePath, bus, address, false)) ++ { ++ createDevice(devicePath, parameters, constructor); ++ tryAgain = true; ++ } ++ else if (createsHWMon && !deviceIsCreated(devicePath, bus, address, true)) ++ { ++ // device is present, hwmon subdir missing ++ deleteDevice(devicePath, address, destructor); ++ tryAgain = true; ++ } ++ ++ if (tryAgain) ++ { ++ std::shared_ptr<boost::asio::steady_timer> createTimer = ++ std::make_shared<boost::asio::steady_timer>(io); ++ createTimer->expires_after(std::chrono::milliseconds(500)); ++ createTimer->async_wait([createTimer, devicePath, parameters, bus, ++ address, constructor, destructor, createsHWMon, ++ retries](const boost::system::error_code&) { ++ buildDevice(devicePath, parameters, bus, address, constructor, ++ destructor, createsHWMon, retries - 1); ++ }); ++ } ++ return 0; ++} ++ + void exportDevice(const std::string& type, + const devices::ExportTemplate& exportTemplate, + const nlohmann::json& configuration) + { + + std::string parameters = exportTemplate.parameters; +- std::string device = exportTemplate.device; ++ std::string devicePath = exportTemplate.devicePath; ++ std::string constructor = exportTemplate.add; ++ std::string destructor = exportTemplate.remove; ++ bool createsHWMon = exportTemplate.createsHWMon; + std::string name = "unknown"; +- const uint64_t* bus = nullptr; +- const uint64_t* address = nullptr; ++ std::shared_ptr<uint64_t> bus = nullptr; ++ std::shared_ptr<uint64_t> address = nullptr; + const nlohmann::json::array_t* channels = nullptr; + + for (auto keyPair = configuration.begin(); keyPair != configuration.end(); +@@ -144,11 +281,13 @@ void exportDevice(const std::string& type, + + if (keyPair.key() == "Bus") + { +- bus = keyPair.value().get_ptr<const uint64_t*>(); ++ bus = std::make_shared<uint64_t>( ++ *keyPair.value().get_ptr<const uint64_t*>()); + } + else if (keyPair.key() == "Address") + { +- address = keyPair.value().get_ptr<const uint64_t*>(); ++ address = std::make_shared<uint64_t>( ++ *keyPair.value().get_ptr<const uint64_t*>()); + } + else if (keyPair.key() == "ChannelNames") + { +@@ -157,49 +296,14 @@ void exportDevice(const std::string& type, + } + boost::replace_all(parameters, TEMPLATE_CHAR + keyPair.key(), + subsituteString); +- boost::replace_all(device, TEMPLATE_CHAR + keyPair.key(), ++ boost::replace_all(devicePath, TEMPLATE_CHAR + keyPair.key(), + subsituteString); + } + +- // if we found bus and address we can attempt to prevent errors +- if (bus != nullptr && address != nullptr) +- { +- std::ostringstream hex; +- hex << std::hex << *address; +- const std::string& addressHex = hex.str(); +- std::string busStr = std::to_string(*bus); ++ int err = buildDevice(devicePath, parameters, bus, address, constructor, ++ destructor, createsHWMon); + +- std::filesystem::path devicePath(device); +- std::filesystem::path parentPath = devicePath.parent_path(); +- if (std::filesystem::is_directory(parentPath)) +- { +- for (const auto& path : +- std::filesystem::directory_iterator(parentPath)) +- { +- if (!std::filesystem::is_directory(path)) +- { +- continue; +- } +- +- const std::string& directoryName = path.path().filename(); +- if (boost::starts_with(directoryName, busStr) && +- boost::ends_with(directoryName, addressHex)) +- { +- return; // already exported +- } +- } +- } +- } +- +- std::ofstream deviceFile(device); +- if (!deviceFile.good()) +- { +- std::cerr << "Error writing " << device << "\n"; +- return; +- } +- deviceFile << parameters; +- deviceFile.close(); +- if (boost::ends_with(type, "Mux") && bus && address && channels) ++ if (!err && boost::ends_with(type, "Mux") && bus && address && channels) + { + linkMux(name, static_cast<size_t>(*bus), static_cast<size_t>(*address), + *channels); +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0002-Entity-manager-Add-support-to-update-assetTag.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0002-Entity-manager-Add-support-to-update-assetTag.patch new file mode 100644 index 000000000..0fea3e8a0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0002-Entity-manager-Add-support-to-update-assetTag.patch @@ -0,0 +1,176 @@ +From 0941036f4206d74bfc3d3e505a5d269fb39c48ff Mon Sep 17 00:00:00 2001 +From: mansijos <mansi.joshi@intel.com> +Date: Tue, 6 Apr 2021 02:12:56 +0530 +Subject: [PATCH] Entity-manager: Add support to update assetTag + +Asset tag is an updateable property from User level interface like +Redfish. User-level interface will update Asset tag in entity-manager, +which will further update the needed FRU interface property exposed. + +Tested: +Successfully updated in assetTag interface as well as in fru interface +while using set-property and using redfish as well. +The new value is preserved after BMC resets. + +Change-Id: If7fbfd8325488280f500ab0e2c8b38475813cc3f +Signed-off-by: mansijos <mansi.joshi@intel.com> +--- + src/EntityManager.cpp | 95 +++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 91 insertions(+), 4 deletions(-) + +diff --git a/src/EntityManager.cpp b/src/EntityManager.cpp +index 490c0f5..139ba9a 100644 +--- a/src/EntityManager.cpp ++++ b/src/EntityManager.cpp +@@ -48,9 +48,19 @@ constexpr const char* lastConfiguration = "/tmp/configuration/last.json"; + constexpr const char* currentConfiguration = "/var/configuration/system.json"; + constexpr const char* globalSchema = "global.json"; + constexpr const int32_t maxMapperDepth = 0; ++constexpr const char* foundObject = "FoundProbe"; + + constexpr const bool debug = false; + ++ ++ ++using foundProbeData = std::map<std::string, std::string>; ++static foundProbeData foundData; ++static std::map<std::string, foundProbeData> mapFoundData; ++ ++constexpr const char* fruConn = "xyz.openbmc_project.FruDevice"; ++constexpr const char* fruIntf = "xyz.openbmc_project.FruDevice"; ++ + struct CmpStr + { + bool operator()(const char* a, const char* b) const +@@ -577,6 +587,43 @@ void addArrayToDbus(const std::string& name, const nlohmann::json& array, + } + } + ++template <typename PropertyType> ++bool persistAssetTag(const PropertyType& newVal, ++ const std::string& jsonPointerString) ++{ ++ std::size_t found = jsonPointerString.find_last_of("/\\"); ++ std::string jsonPointerPath = jsonPointerString.substr(0, found); ++ ++ auto it = mapFoundData.find(jsonPointerPath); ++ if (it == mapFoundData.end()) ++ { ++ std::cerr << "Error in finding jsonPointerPath in mapFoundData" ++ << "\n"; ++ return false; ++ } ++ ++ foundProbeData& tmpMap = it->second; ++ auto foundPath = tmpMap.find("foundPath"); ++ if (foundPath == tmpMap.end()) ++ { ++ std::cerr << "No prob object data is avaliable in foundProbeData" ++ << "\n"; ++ return false; ++ } ++ ++ systemBus->async_method_call( ++ [](const boost::system::error_code& ec) { ++ if (ec) ++ { ++ std::cerr << "Error setting AssetTag in FRU interface " << ec ++ << "\n"; ++ } ++ }, ++ fruConn, foundPath->second, "org.freedesktop.DBus.Properties", "Set", ++ fruIntf, "PRODUCT_ASSET_TAG", std::variant<PropertyType>(newVal)); ++ return true; ++} ++ + template <typename PropertyType> + void addProperty(const std::string& propertyName, const PropertyType& value, + sdbusplus::asio::dbus_interface* iface, +@@ -591,9 +638,18 @@ void addProperty(const std::string& propertyName, const PropertyType& value, + } + iface->register_property( + propertyName, value, +- [&systemConfiguration, ++ [propertyName, &systemConfiguration, + jsonPointerString{std::string(jsonPointerString)}]( + const PropertyType& newVal, PropertyType& val) { ++ if (propertyName == "AssetTag") ++ { ++ if (!persistAssetTag(newVal, jsonPointerString)) ++ { ++ std::cerr << "error setting AssetTag in FRU interface\n"; ++ return -1; ++ } ++ } ++ + val = newVal; + if (!setJsonFromPointer(jsonPointerString, val, + systemConfiguration)) +@@ -993,6 +1049,9 @@ void postToDbus(const nlohmann::json& newConfiguration, + populateInterfaceFromJson(systemConfiguration, jsonPointerPath, + boardIface, boardValues, objServer); + jsonPointerPath += "/"; ++ ++ std::string foundPath; ++ + // iterate through board properties + for (auto& boardField : boardValues.items()) + { +@@ -1002,9 +1061,28 @@ void postToDbus(const nlohmann::json& newConfiguration, + createInterface(objServer, boardName, boardField.key(), + boardKeyOrig); + +- populateInterfaceFromJson(systemConfiguration, +- jsonPointerPath + boardField.key(), +- iface, boardField.value(), objServer); ++ if (boardField.key() == "FoundProbe") ++ { ++ foundPath = boardField.value()["Path"]; ++ } ++ if (boardField.key() == ++ "xyz.openbmc_project.Inventory.Decorator.AssetTag") ++ { ++ foundData["foundPath"] = foundPath; ++ mapFoundData[jsonPointerPath + boardField.key()] = ++ foundData; ++ ++ populateInterfaceFromJson( ++ systemConfiguration, jsonPointerPath + boardField.key(), ++ iface, boardField.value(), objServer, ++ sdbusplus::asio::PropertyPermission::readWrite); ++ } ++ else ++ { ++ populateInterfaceFromJson( ++ systemConfiguration, jsonPointerPath + boardField.key(), ++ iface, boardField.value(), objServer); ++ } + } + } + +@@ -1362,6 +1440,11 @@ void PerformScan::run() + { + continue; // non-numeric replacement + } ++ ++ nlohmann::json recordVal = *recordPtr; ++ // Save the dbus path info of the device ++ recordVal[foundObject]["Path"] = std::get<1>(*itr); ++ + usedNames.insert(nameIt.value()); + auto usedIt = std::find(indexes.begin(), + indexes.end(), index); +@@ -1439,6 +1522,10 @@ void PerformScan::run() + } + } + ++ // Save the dbus path info of the device ++ record[foundObject]["Path"] = ++ std::get<1>(foundDeviceAndPath); ++ + if (replaceStr) + { + std::cerr << "Duplicates found, replacing " +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0003-Add-logs-to-fwVersionIsSame.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0003-Add-logs-to-fwVersionIsSame.patch new file mode 100644 index 000000000..94af67967 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0003-Add-logs-to-fwVersionIsSame.patch @@ -0,0 +1,56 @@ +From 28525b56161e1b659e85e85c33fc00dc397758aa Mon Sep 17 00:00:00 2001 +From: Helen Huang <he.huang@intel.com> +Date: Mon, 19 Apr 2021 16:06:15 +0800 +Subject: [PATCH] Add logs to fwVersionIsSame() + +Add logs to fwVersionIsSame() to indicate whether the firmware +version is changed or not. + +Tested: +Logs are printed as expected when firmware updating and BMC rebooting. + +Log of rebooting: +The firmware version is similiar as the last boot, +Hash value of versionFile is:3336889560 + +Log of Firmware update: +The firmware version is changed since the last boot, +hash value of current versionFile is:3336889560, +hash value of versionFile of last boot is:834871226 + +Change-Id: I5306917329d2e2e015af58cad1e9c59881f0b217 +Signed-off-by: Helen Huang <he.huang@intel.com> +--- + include/Utils.hpp | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/include/Utils.hpp b/include/Utils.hpp +index 657af92..8238807 100644 +--- a/include/Utils.hpp ++++ b/include/Utils.hpp +@@ -116,13 +116,22 @@ inline bool fwVersionIsSame(void) + + if (expectedHash == hashString) + { ++ std::cout << "The firmware version is similiar as the last boot, " ++ "hash value of versionFile is:" ++ << hashString.c_str() << "\n"; + return true; + } ++ std::cout << "The firmware version is changed since the last boot, hash " ++ "value of current versionFile is:" ++ << expectedHash.c_str() ++ << ", hash value of versionFile of last boot is:" ++ << hashString.c_str() << "\n"; + hashFile.close(); + } + + std::ofstream output(versionHashFile); + output << expectedHash; ++ + return false; + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0004-Adding-MUX-and-Drives-present-in-HSBP-in-json-config.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0004-Adding-MUX-and-Drives-present-in-HSBP-in-json-config.patch new file mode 100644 index 000000000..4f6679dde --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0004-Adding-MUX-and-Drives-present-in-HSBP-in-json-config.patch @@ -0,0 +1,149 @@ +From 95a1f9e5f65d35adc3cf8d3b3095d92b63c17f85 Mon Sep 17 00:00:00 2001 +From: AKSHAY RAVEENDRAN K <akshay.raveendran.k@intel.com> +Date: Sun, 20 Jun 2021 18:22:34 +0000 +Subject: [PATCH] Add MUX and Drives present in HSBP in json configuration + +Added Mux addresses for all 3 HSBP configuration and the drive +names to be exposed via entity manager when the HSBP is connected + +Added board instance to separate each HSBP board. + +Tested: +After this addition, detected the MUXes in entity manager +tree as well as in /dev/i2c-mux location. Also able to detect +the drive address. + +Change-Id: Ic07e3880cf5b6f8a47ee7b8f1f98e12042765da8 +Signed-off-by: AKSHAY RAVEENDRAN K <akshay.raveendran.k@intel.com> + +--- + configurations/F2U8X25 HSBP.json | 78 ++++++++++++++++++++++++++++++-- + 1 file changed, 75 insertions(+), 3 deletions(-) + +diff --git a/configurations/F2U8X25 HSBP.json b/configurations/F2U8X25 HSBP.json +index c6c7678..e2eedfa 100644 +--- a/configurations/F2U8X25 HSBP.json ++++ b/configurations/F2U8X25 HSBP.json +@@ -7,6 +7,30 @@ + "Name": "F2U8X25 HSBP1 FRU", + "Type": "EEPROM" + }, ++ { ++ "Address": "0x70", ++ "Bus": "$bus", ++ "ChannelNames": [ ++ "Drive_1", ++ "Drive_2", ++ "Drive_3", ++ "Drive_4" ++ ], ++ "Name": "Drive Mux 1", ++ "Type": "PCA9546Mux" ++ }, ++ { ++ "Address": "0x74", ++ "Bus": "$bus", ++ "ChannelNames": [ ++ "Drive_5", ++ "Drive_6", ++ "Drive_7", ++ "Drive_8" ++ ], ++ "Name": "Drive Mux 2", ++ "Type": "PCA9546Mux" ++ }, + { + "Address": "0x48", + "Bus": "$bus", +@@ -40,7 +64,7 @@ + "Type": "TMP75" + } + ], +- "Name": "F2U8X25 HSBP", ++ "Name": "F2U8X25 HSBP 1", + "Probe": "xyz.openbmc_project.FruDevice({'BOARD_PRODUCT_NAME': 'F2U8X25PCIHSBP', 'ADDRESS': 80})", + "Type": "Board", + "xyz.openbmc_project.Inventory.Decorator.Asset": { +@@ -58,6 +82,30 @@ + "Name": "F2U8X25 HSBP2 FRU", + "Type": "EEPROM" + }, ++ { ++ "Address": "0x73", ++ "Bus": "$bus", ++ "ChannelNames": [ ++ "Drive_9", ++ "Drive_10", ++ "Drive_11", ++ "Drive_12" ++ ], ++ "Name": "Drive Mux 3", ++ "Type": "PCA9546Mux" ++ }, ++ { ++ "Address": "0x77", ++ "Bus": "$bus", ++ "ChannelNames": [ ++ "Drive_13", ++ "Drive_14", ++ "Drive_15", ++ "Drive_16" ++ ], ++ "Name": "Drive Mux 4", ++ "Type": "PCA9546Mux" ++ }, + { + "Address": "0x4B", + "Bus": "$bus", +@@ -91,7 +139,7 @@ + "Type": "TMP75" + } + ], +- "Name": "F2U8X25 HSBP", ++ "Name": "F2U8X25 HSBP 2", + "Probe": "xyz.openbmc_project.FruDevice({'BOARD_PRODUCT_NAME': 'F2U8X25PCIHSBP', 'ADDRESS': 83})", + "Type": "Board", + "xyz.openbmc_project.Inventory.Decorator.Asset": { +@@ -109,6 +157,30 @@ + "Name": "F2U8X25 HSBP3 FRU", + "Type": "EEPROM" + }, ++ { ++ "Address": "0x72", ++ "Bus": "$bus", ++ "ChannelNames": [ ++ "Drive_17", ++ "Drive_18", ++ "Drive_19", ++ "Drive_20" ++ ], ++ "Name": "Drive Mux 5", ++ "Type": "PCA9546Mux" ++ }, ++ { ++ "Address": "0x76", ++ "Bus": "$bus", ++ "ChannelNames": [ ++ "Drive_21", ++ "Drive_22", ++ "Drive_23", ++ "Drive_24" ++ ], ++ "Name": "Drive Mux 6", ++ "Type": "PCA9546Mux" ++ }, + { + "Address": "0x4A", + "Bus": "$bus", +@@ -142,7 +214,7 @@ + "Type": "TMP75" + } + ], +- "Name": "F2U8X25 HSBP", ++ "Name": "F2U8X25 HSBP 3", + "Probe": "xyz.openbmc_project.FruDevice({'BOARD_PRODUCT_NAME': 'F2U8X25PCIHSBP', 'ADDRESS': 82})", + "Type": "Board", + "xyz.openbmc_project.Inventory.Decorator.Asset": { +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0005-Allow-MUX-idle-state-to-be-configured-as-DISCONNECT.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0005-Allow-MUX-idle-state-to-be-configured-as-DISCONNECT.patch new file mode 100644 index 000000000..ac0995614 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0005-Allow-MUX-idle-state-to-be-configured-as-DISCONNECT.patch @@ -0,0 +1,131 @@ +From aada39602a21e83d7e8c39c39fb8c5c32122863c Mon Sep 17 00:00:00 2001 +From: AKSHAY RAVEENDRAN K <akshay.raveendran.k@intel.com> +Date: Thu, 2 Sep 2021 11:11:40 +0000 +Subject: [PATCH] Allow MUX idle state to be configured as DISCONNECT + +The existing Linux behavior is to leave the Mux status as it is after +an operation. In HSBP and in other places we have more than one MUX +parallel in same root bus. The existing behavior will result in reading +multiple buses of different MUXes at the same time and causes bad read. +In this fix, we can configure "MuxIdleMode" as "Disconnect" in +configuration file as shown below + +{ + "Address": "0x70", + "Bus": "$bus", + "ChannelNames": [ + "Drive_1", + "Drive_2", + "Drive_3", + "Drive_4" + ], + "MuxIdleMode": "Disconnect", + "Name": "Drive Mux 1", + "Type": "PCA9546Mux" + +Tested: +Set the MUX idle mode to Disconnect in MUXes present in HSBP board and +only one channel is read at a time. + +Change-Id: I6d29cd3be350682c386bd3072e76b930a7d45587 +Signed-off-by: AKSHAY RAVEENDRAN K <akshay.raveendran.k@intel.com> +--- + src/Overlay.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 52 insertions(+), 3 deletions(-) + +diff --git a/src/Overlay.cpp b/src/Overlay.cpp +index 4454423..2843c31 100644 +--- a/src/Overlay.cpp ++++ b/src/Overlay.cpp +@@ -38,11 +38,46 @@ constexpr const char* outputDir = "/tmp/overlays"; + constexpr const char* templateChar = "$"; + constexpr const char* i2CDevsDir = "/sys/bus/i2c/devices"; + constexpr const char* muxSymlinkDir = "/dev/i2c-mux"; ++constexpr const char* idleModeAsIs = "-1"; ++constexpr const char* idleModeDisconnect = "-2"; + + constexpr const bool debug = false; + + std::regex illegalNameRegex("[^A-Za-z0-9_]"); + ++void setIdleMode(const std::string& muxName, size_t busIndex, size_t address, ++ const std::string& idleMode) ++{ ++ std::error_code ec; ++ ++ std::ostringstream hexAddress; ++ hexAddress << std::hex << std::setfill('0') << std::setw(4) << address; ++ ++ std::filesystem::path idlePath(i2CDevsDir); ++ idlePath /= ++ std::to_string(busIndex) + "-" + hexAddress.str() + "/idle_state"; ++ ++ std::string modeData = ++ (idleMode == "Disconnect") ? idleModeDisconnect : idleModeAsIs; ++ ++ if (debug) ++ { ++ std::cerr << "Setting " << muxName << " idle state to " << modeData ++ << " in " << idlePath << "\n"; ++ } ++ ++ std::ofstream idleFile(idlePath); ++ if (!idleFile.good()) ++ { ++ std::cerr << "Can't set idle mode in " << idlePath << " for " << muxName ++ << "\n"; ++ } ++ else ++ { ++ idleFile << modeData; ++ } ++} ++ + // helper function to make json types into string + std::string jsonToString(const nlohmann::json& in) + { +@@ -285,6 +320,7 @@ void exportDevice(const std::string& type, + std::shared_ptr<uint64_t> bus = nullptr; + std::shared_ptr<uint64_t> address = nullptr; + const nlohmann::json::array_t* channels = nullptr; ++ std::string idleMode; + + for (auto keyPair = configuration.begin(); keyPair != configuration.end(); + keyPair++) +@@ -318,6 +354,11 @@ void exportDevice(const std::string& type, + channels = + keyPair.value().get_ptr<const nlohmann::json::array_t*>(); + } ++ else if (keyPair.key() == "MuxIdleMode") ++ { ++ idleMode = keyPair.value().get<std::string>(); ++ } ++ + boost::replace_all(parameters, templateChar + keyPair.key(), + subsituteString); + boost::replace_all(devicePath, templateChar + keyPair.key(), +@@ -327,10 +368,18 @@ void exportDevice(const std::string& type, + int err = buildDevice(devicePath, parameters, bus, address, constructor, + destructor, createsHWMon); + +- if (!err && boost::ends_with(type, "Mux") && bus && address && channels) ++ if (!err && boost::ends_with(type, "Mux") && bus && address) + { +- linkMux(name, static_cast<size_t>(*bus), static_cast<size_t>(*address), +- *channels); ++ if (channels) ++ { ++ linkMux(name, static_cast<size_t>(*bus), ++ static_cast<size_t>(*address), *channels); ++ } ++ if (!idleMode.empty()) ++ { ++ setIdleMode(name, static_cast<size_t>(*bus), ++ static_cast<size_t>(*address), idleMode); ++ } + } + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0006-Change-HSBP-FRU-address-and-add-MUX-mode-configurati.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0006-Change-HSBP-FRU-address-and-add-MUX-mode-configurati.patch new file mode 100644 index 000000000..a3d065fd9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0006-Change-HSBP-FRU-address-and-add-MUX-mode-configurati.patch @@ -0,0 +1,96 @@ +From 0ece2ae628f6d4ee57319dc7e153680cdddff1d2 Mon Sep 17 00:00:00 2001 +From: AKSHAY RAVEENDRAN K <akshay.raveendran.k@intel.com> +Date: Sun, 12 Sep 2021 22:21:55 +0000 +Subject: [PATCH] Change HSBP FRU address and add MUX mode configuration + +Changed the HSBP EEPROM FRU address according to Hardware +rework and added the MUX idle mode configuration as +"Disconnect". The later will keep MUX channel mode in +disconnected state after the channel is accessed. + +Tested: +1. Detected and read the HSBP EEPROM FRU with new address +on reworked board. +2. Confirmed the idle state of MUX channel after it is +accessed is disconnected, this solved the bad read +caused by reading multiple buses of different MUXes at +same time. + +Signed-off-by: AKSHAY RAVEENDRAN K <akshay.raveendran.k@intel.com> +--- + configurations/F2U8X25 HSBP.json | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/configurations/F2U8X25 HSBP.json b/configurations/F2U8X25 HSBP.json +index e2eedfa..60e7817 100644 +--- a/configurations/F2U8X25 HSBP.json ++++ b/configurations/F2U8X25 HSBP.json +@@ -16,6 +16,7 @@ + "Drive_3", + "Drive_4" + ], ++ "MuxIdleMode": "Disconnect", + "Name": "Drive Mux 1", + "Type": "PCA9546Mux" + }, +@@ -28,6 +29,7 @@ + "Drive_7", + "Drive_8" + ], ++ "MuxIdleMode": "Disconnect", + "Name": "Drive Mux 2", + "Type": "PCA9546Mux" + }, +@@ -65,7 +67,7 @@ + } + ], + "Name": "F2U8X25 HSBP 1", +- "Probe": "xyz.openbmc_project.FruDevice({'BOARD_PRODUCT_NAME': 'F2U8X25PCIHSBP', 'ADDRESS': 80})", ++ "Probe": "xyz.openbmc_project.FruDevice({'BOARD_PRODUCT_NAME': 'F2U8X25PCIHSBP', 'ADDRESS': 84})", + "Type": "Board", + "xyz.openbmc_project.Inventory.Decorator.Asset": { + "Manufacturer": "$BOARD_MANUFACTURER", +@@ -91,6 +93,7 @@ + "Drive_11", + "Drive_12" + ], ++ "MuxIdleMode": "Disconnect", + "Name": "Drive Mux 3", + "Type": "PCA9546Mux" + }, +@@ -103,6 +106,7 @@ + "Drive_15", + "Drive_16" + ], ++ "MuxIdleMode": "Disconnect", + "Name": "Drive Mux 4", + "Type": "PCA9546Mux" + }, +@@ -140,7 +144,7 @@ + } + ], + "Name": "F2U8X25 HSBP 2", +- "Probe": "xyz.openbmc_project.FruDevice({'BOARD_PRODUCT_NAME': 'F2U8X25PCIHSBP', 'ADDRESS': 83})", ++ "Probe": "xyz.openbmc_project.FruDevice({'BOARD_PRODUCT_NAME': 'F2U8X25PCIHSBP', 'ADDRESS': 87})", + "Type": "Board", + "xyz.openbmc_project.Inventory.Decorator.Asset": { + "Manufacturer": "$BOARD_MANUFACTURER", +@@ -166,6 +170,7 @@ + "Drive_19", + "Drive_20" + ], ++ "MuxIdleMode": "Disconnect", + "Name": "Drive Mux 5", + "Type": "PCA9546Mux" + }, +@@ -178,6 +183,7 @@ + "Drive_23", + "Drive_24" + ], ++ "MuxIdleMode": "Disconnect", + "Name": "Drive Mux 6", + "Type": "PCA9546Mux" + }, +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0007-Add-HSBP-FRU-details-in-json-configuration.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0007-Add-HSBP-FRU-details-in-json-configuration.patch new file mode 100644 index 000000000..9ad2641bc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/0007-Add-HSBP-FRU-details-in-json-configuration.patch @@ -0,0 +1,78 @@ +From ff4b6cab1dead31bad114321c211797ac28b36b1 Mon Sep 17 00:00:00 2001 +From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +Date: Wed, 10 Nov 2021 15:17:13 +0000 +Subject: [PATCH] Add HSBP FRU details in json configuration + +Added Intel specific HSBP CPLD FRU details in json configuration file. + +Tested: +1. Detected and read the HSBP FRU with new address on reworked board. +2. busctl tree xyz.openbmc_project.HsbpManager +└─/xyz + └─/xyz/openbmc_project + ├─/xyz/openbmc_project/inventory + │ └─/xyz/openbmc_project/inventory/item + │ ├─/xyz/openbmc_project/inventory/item/drive + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_1 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_10 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_11 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_12 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_13 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_14 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_15 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_16 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_2 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_3 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_4 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_5 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_6 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_7 + │ │ ├─/xyz/openbmc_project/inventory/item/drive/Drive_8 + │ │ └─/xyz/openbmc_project/inventory/item/drive/Drive_9 + │ └─/xyz/openbmc_project/inventory/item/hsbp + │ ├─/xyz/openbmc_project/inventory/item/hsbp/HSBP_1 + │ └─/xyz/openbmc_project/inventory/item/hsbp/HSBP_2 + └─/xyz/openbmc_project/software + ├─/xyz/openbmc_project/software/HSBP_1 + └─/xyz/openbmc_project/software/HSBP_2 + +Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +--- + configurations/F2U8X25 HSBP.json | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/configurations/F2U8X25 HSBP.json b/configurations/F2U8X25 HSBP.json +index 60e7817..cdf9076 100644 +--- a/configurations/F2U8X25 HSBP.json ++++ b/configurations/F2U8X25 HSBP.json +@@ -7,6 +7,13 @@ + "Name": "F2U8X25 HSBP1 FRU", + "Type": "EEPROM" + }, ++ { ++ "Address": "0x68", ++ "Bus": "$bus", ++ "Index": 1, ++ "Name": "HSBP 1", ++ "Type": "Intel HSBP CPLD" ++ }, + { + "Address": "0x70", + "Bus": "$bus", +@@ -84,6 +91,13 @@ + "Name": "F2U8X25 HSBP2 FRU", + "Type": "EEPROM" + }, ++ { ++ "Address": "0x69", ++ "Bus": "$bus", ++ "Index": 2, ++ "Name": "HSBP 2", ++ "Type": "Intel HSBP CPLD" ++ }, + { + "Address": "0x73", + "Bus": "$bus", +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend new file mode 100644 index 000000000..ba51f9454 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend @@ -0,0 +1,14 @@ +# this is here just to bump faster than upstream +# SRC_URI = "git://github.com/openbmc/entity-manager.git" +SRCREV = "2c412eef8eb76bf2a998c9d193f2dc92aaec39f8" + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI += " file://0002-Entity-manager-Add-support-to-update-assetTag.patch \ + file://0003-Add-logs-to-fwVersionIsSame.patch \ + file://0004-Adding-MUX-and-Drives-present-in-HSBP-in-json-config.patch \ + file://0005-Allow-MUX-idle-state-to-be-configured-as-DISCONNECT.patch \ + file://0006-Change-HSBP-FRU-address-and-add-MUX-mode-configurati.patch \ + file://0007-Add-HSBP-FRU-details-in-json-configuration.patch \ + " + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/obmc-console.conf b/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/obmc-console.conf new file mode 100644 index 000000000..1d332e2a2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/obmc-console.conf @@ -0,0 +1,3 @@ +baud = 921600 +local-tty = ttyS3 +local-tty-baud = 921600 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/obmc-console@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/obmc-console@.service new file mode 100644 index 000000000..4ec453d9e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/obmc-console@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Phosphor Console Muxer listening on device /dev/%I +BindsTo=dev-%i.device +After=dev-%i.device + +[Service] +ExecStartPre=/usr/bin/sol-option-check.sh +ExecStart=/usr/bin/env obmc-console-server --config {sysconfdir}/obmc-console.conf %i +ExecStopPost=/bin/sh -c 'echo -n "0" > /sys/bus/platform/drivers/aspeed-uart-routing/1e789098.uart-routing/hicra' +SyslogIdentifier=obmc-console-server +Restart=always + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/sol-option-check.sh b/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/sol-option-check.sh new file mode 100755 index 000000000..19179c497 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/sol-option-check.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# Copyright 2017-2020 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +CPUPART="CPU part" +AST2500_ID="0xb76" +AST2600_ID="0xc07" + +if ([ $(grep "$CPUPART" /proc/cpuinfo | grep "$AST2500_ID" | wc -l) != 0 ] && \ + [ $(grep 192000000 /sys/class/tty/ttyS0/uartclk | wc -l) != 0 ]) || \ + ([ $(grep "$CPUPART" /proc/cpuinfo | grep "$AST2600_ID" | wc -l) != 0 ] && \ + [ $(grep 14769216 /sys/class/tty/ttyS0/uartclk | wc -l) != 0 ]); then + echo "hs-uart" + sed -i -e 's/115200/921600/g' /etc/obmc-console.conf +else + echo "normal uart" + sed -i -e 's/921600/115200/g' /etc/obmc-console.conf +fi diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console_%.bbappend new file mode 100644 index 000000000..9a0eae176 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console_%.bbappend @@ -0,0 +1,17 @@ +FILESEXTRAPATHS:append := ":${THISDIR}/${PN}" +OBMC_CONSOLE_HOST_TTY = "ttyS2" +SRC_URI += "file://sol-option-check.sh \ + file://obmc-console@.service \ + " +inherit obmc-phosphor-systemd + +SYSTEMD_SERVICE:${PN} += " \ + ${PN}@${OBMC_CONSOLE_HOST_TTY}.service \ + " + +do_install:append() { + rm -rf ${D}${base_libdir}/udev/rules.d/80-obmc-console-uart.rules + install -m 0644 ${WORKDIR}/${PN}@.service ${D}${systemd_system_unitdir} + install -d ${D}${bindir} + install -m 0755 ${WORKDIR}/sol-option-check.sh ${D}${bindir} +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync.bb new file mode 100644 index 000000000..83630fc36 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync.bb @@ -0,0 +1,24 @@ + +SUMMARY = "PCH BMC time service" +DESCRIPTION = "This service will read date/time from PCH device periodically, and set the BMC system time accordingly" + +SRC_URI = "\ + file://CMakeLists.txt;subdir=${BP} \ + file://pch-time-sync.cpp;subdir=${BP} \ + " +PV = "0.1" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SYSTEMD_SERVICE:${PN} = "pch-time-sync.service" + +inherit cmake +inherit obmc-phosphor-systemd + +DEPENDS += " \ + sdbusplus \ + phosphor-logging \ + boost \ + i2c-tools \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/.clang-format b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/.clang-format new file mode 100644 index 000000000..dd2770837 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/.clang-format @@ -0,0 +1,98 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +PointerAlignment: Left +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^[<"](gtest|gmock)' + Priority: 5 + - Regex: '^"config.h"' + Priority: -1 + - Regex: '^".*\.hpp"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/CMakeLists.txt new file mode 100644 index 000000000..a4cf8155f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required (VERSION 3.5 FATAL_ERROR) +project (pch-time-sync CXX) +set (CMAKE_CXX_STANDARD 17) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti") + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +# boost support +find_package (Boost REQUIRED) +# pkg_check_modules(Boost boost REQUIRED) +include_directories (${Boost_INCLUDE_DIRS}) +add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY) +add_definitions (-DBOOST_SYSTEM_NO_DEPRECATED) +add_definitions (-DBOOST_ALL_NO_LIB) +add_definitions (-DBOOST_NO_RTTI) +add_definitions (-DBOOST_NO_TYPEID) +add_definitions (-DBOOST_ASIO_DISABLE_THREADS) + +# import sdbusplus +find_package (PkgConfig REQUIRED) +pkg_check_modules (SDBUSPLUSPLUS sdbusplus REQUIRED) +include_directories (${SDBUSPLUSPLUS_INCLUDE_DIRS}) +link_directories (${SDBUSPLUSPLUS_LIBRARY_DIRS}) + +# import phosphor-logging +find_package (PkgConfig REQUIRED) +pkg_check_modules (LOGGING phosphor-logging REQUIRED) +include_directories (${LOGGING_INCLUDE_DIRS}) +link_directories (${LOGGING_LIBRARY_DIRS}) + +add_executable (pch-time-sync pch-time-sync.cpp) + +target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES}) +target_link_libraries (${PROJECT_NAME} ${SDBUSPLUSPLUS_LIBRARIES} + phosphor_logging) +target_link_libraries (${PROJECT_NAME} i2c) + +install (TARGETS pch-time-sync DESTINATION bin) diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/cmake-format.json b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/cmake-format.json new file mode 100644 index 000000000..583c255a3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/cmake-format.json @@ -0,0 +1,12 @@ +{ + "enum_char": ".", + "line_ending": "unix", + "bullet_char": "*", + "max_subargs_per_line": 99, + "command_case": "lower", + "tab_size": 4, + "line_width": 80, + "separate_fn_name_with_space": true, + "dangle_parens": true, + "separate_ctrl_name_with_space": true +}
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.cpp b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.cpp new file mode 100644 index 000000000..454bdc1db --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.cpp @@ -0,0 +1,431 @@ +/* Copyright 2019 Intel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <systemd/sd-journal.h> +#include <time.h> + +#include <boost/asio/io_service.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <chrono> +#include <filesystem> +#include <iostream> +#include <phosphor-logging/log.hpp> +#include <sdbusplus/asio/object_server.hpp> +extern "C" { +#include <i2c/smbus.h> +#include <linux/i2c-dev.h> +} + +static constexpr uint32_t syncIntervalNormalMS = 60000; +static constexpr uint32_t syncIntervalFastMS = (syncIntervalNormalMS / 2); +static constexpr uint32_t syncIntervalBootMS = 5000; + +static uint32_t syncIntervalMS = syncIntervalNormalMS; + +// will update bmc time if the time difference beyond this value +static constexpr uint8_t timeDiffAllowedSecond = 1; +static uint8_t pchDevI2cBusNo = 0; +static uint8_t pchDevI2cSlaveAddr = 0; +static bool getPCHI2cAddrFlag = false; +static constexpr const char* clockFile = "/var/lib/systemd/timesync/clock"; +static inline uint8_t bcd2Decimal(uint8_t hex) +{ + uint8_t dec = ((hex & 0xF0) >> 4) * 10 + (hex & 0x0F); + return dec; +} + +class I2CFile +{ + private: + int fd = -1; + + public: + I2CFile(const int& i2cBus, const int& slaveAddr, const int& flags) + { + std::string i2cDev = "/dev/i2c-" + std::to_string(i2cBus); + + fd = open(i2cDev.c_str(), flags); + if (fd < 0) + { + throw std::runtime_error("Unable to open i2c device."); + } + + if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0) + { + close(fd); + fd = -1; + throw std::runtime_error("Unable to set i2c slave address."); + } + } + + uint8_t i2cReadByteData(const uint8_t& offset) + { + int ret = i2c_smbus_read_byte_data(fd, offset); + + if (ret < 0) + { + throw std::runtime_error("i2c read failed"); + } + return static_cast<uint8_t>(ret); + } + + ~I2CFile() + { + if (!(fd < 0)) + { + close(fd); + } + } +}; + +static void getPCHI2cAddr(std::shared_ptr<sdbusplus::asio::connection>& conn, + const std::string& service, const std::string& object, + const std::string& interface) +{ + conn->async_method_call( + [](boost::system::error_code ec, + const std::vector< + std::pair<std::string, std::variant<std::string, uint64_t>>>& + propertiesList) { + if (ec) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "DBUS response error: cannot get I2c address of PCH timer", + phosphor::logging::entry("ECVALUE=%x", ec.value()), + phosphor::logging::entry("ECMESSAGE=%s", + ec.message().c_str())); + return; + } + const uint64_t* i2cBusNoValue = nullptr; + const uint64_t* i2cSlaveAddrValue = nullptr; + for (const auto& property : propertiesList) + { + + if (property.first == "PchSmbusSlaveI2cBus") + { + i2cBusNoValue = std::get_if<uint64_t>(&property.second); + } + if (property.first == "PchSmbusSlaveI2cAddress") + { + i2cSlaveAddrValue = std::get_if<uint64_t>(&property.second); + } + } + if ((i2cBusNoValue != nullptr) && (i2cSlaveAddrValue != nullptr)) + { + pchDevI2cBusNo = static_cast<uint8_t>(*i2cBusNoValue); + pchDevI2cSlaveAddr = static_cast<uint8_t>(*i2cSlaveAddrValue); + getPCHI2cAddrFlag = true; + } + }, + service, object, "org.freedesktop.DBus.Properties", "GetAll", + interface); +} + +static void + getPCHTimerConfiguration(std::shared_ptr<sdbusplus::asio::connection>& conn) +{ + conn->async_method_call( + [&conn]( + boost::system::error_code ec, + const std::vector<std::pair< + std::string, + std::vector<std::pair<std::string, std::vector<std::string>>>>>& + subtree) { + if (ec) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "DBUS response error:cannot get PCH configuration", + phosphor::logging::entry("ECVALUE=%x", ec.value()), + phosphor::logging::entry("ECMESSAGE=%s", + ec.message().c_str())); + return; + } + if (subtree.empty()) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "subtree empty"); + return; + } + getPCHI2cAddr(conn, subtree[0].second[0].first, subtree[0].first, + "xyz.openbmc_project.Configuration.PchSmbusSlave"); + return; + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/", 0, + std::array<const char*, 1>{ + "xyz.openbmc_project.Configuration.PchSmbusSlave"}); + + return; +} + +class PCHSync +{ + private: + bool getPCHDate(uint8_t& year, uint8_t& month, uint8_t& day, uint8_t& hour, + uint8_t& minute, uint8_t& second) + { + try + { + constexpr uint8_t pchDevRegRTCYear = 0x0f; + constexpr uint8_t pchDevRegRTCMonth = 0x0e; + constexpr uint8_t pchDevRegRTCDay = 0x0d; + constexpr uint8_t pchDevRegRTCHour = 0x0b; + constexpr uint8_t pchDevRegRTCMinute = 0x0a; + constexpr uint8_t pchDevRegRTCSecond = 0x09; + I2CFile pchDev(pchDevI2cBusNo, pchDevI2cSlaveAddr, + O_RDWR | O_CLOEXEC); + year = pchDev.i2cReadByteData(pchDevRegRTCYear); + year = bcd2Decimal(year); + if (year > 99) + { + return false; + } + + month = pchDev.i2cReadByteData(pchDevRegRTCMonth); + month = bcd2Decimal(month); + if ((month < 1) || (month > 12)) + { + return false; + } + + day = pchDev.i2cReadByteData(pchDevRegRTCDay); + day = bcd2Decimal(day); + if ((day < 1) || (day > 31)) + { + return false; + } + + hour = pchDev.i2cReadByteData(pchDevRegRTCHour); + hour = bcd2Decimal(hour); + if (hour >= 24) + { + return false; + } + + minute = pchDev.i2cReadByteData(pchDevRegRTCMinute); + minute = bcd2Decimal(minute); + if (minute >= 60) + { + return false; + } + + second = pchDev.i2cReadByteData(pchDevRegRTCSecond); + second = bcd2Decimal(second); + if (second >= 60) + { + return false; + } + } + catch (const std::exception& e) + { + return false; + } + + return true; + } + + bool getSystemTime(time_t& timeSeconds) + { + struct timespec sTime = {0}; + int ret = 0; + + ret = clock_gettime(CLOCK_REALTIME, &sTime); + + if (ret != 0) + { + return false; + } + timeSeconds = sTime.tv_sec; + return true; + } + + bool updateClockFileTimestamp() + { + if (!std::filesystem::exists(clockFile)) + { + phosphor::logging::log<phosphor::logging::level::WARNING>( + "The systemd timestamp synchronization file doesn't exist: ", + phosphor::logging::entry("PATHNAME=%s", clockFile)); + return false; + } + int rc = utimensat(AT_FDCWD, clockFile, nullptr, 0); + if (rc) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "utimensat systemd timestamp synchronization file fail: ", + phosphor::logging::entry("PATHNAME=%s", clockFile), + phosphor::logging::entry("ERRCODE=%x", errno)); + return false; + } + return true; + } + bool setSystemTime(uint32_t timeSeconds) + { + struct timespec sTime = {0}; + int ret = 0; + + sTime.tv_sec = timeSeconds; + sTime.tv_nsec = 0; + + ret = clock_settime(CLOCK_REALTIME, &sTime); + + return (ret == 0); + } + + bool updateBMCTime() + { + int ret = 0; + time_t BMCTimeSeconds = 0; + time_t PCHTimeSeconds = 0; + struct tm tm = {0}; + + // get PCH and system time + + if (!getPCHDate(year, month, day, hour, minute, second)) + { + return false; + }; + if (!getSystemTime(BMCTimeSeconds)) + { + return false; + } + // fix error when year is set to 2000-2009. + std::string dateString = + std::to_string(2000 + year) + "-" + std::to_string(month) + "-" + + std::to_string(day) + " " + std::to_string(hour) + ":" + + std::to_string(minute) + ":" + std::to_string(second); + + strptime(dateString.c_str(), "%Y-%m-%d %H:%M:%S", &tm); + + PCHTimeSeconds = mktime(&tm); + if (PCHTimeSeconds == -1) + { + return false; + } + + if (std::abs(PCHTimeSeconds - BMCTimeSeconds) > timeDiffAllowedSecond) + { + if (!setSystemTime(PCHTimeSeconds)) + { + return false; + } + char newDateString[32] = {0}; + strftime(newDateString, sizeof(newDateString), "%F %T", + gmtime(&PCHTimeSeconds)); + + struct tm* timeinfo; + char oldDateString[32] = {0}; + timeinfo = gmtime(&BMCTimeSeconds); + strftime(oldDateString, sizeof(oldDateString), "%F %T", timeinfo); + + // Log event about BMC time update via PCH + sd_journal_send( + "MESSAGE=BMC time updated via PCH: New time=%s, Old time=%s", + newDateString, oldDateString, "PRIORITY=%i", LOG_INFO, + "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.BMCTimeUpdatedViaHost", + "REDFISH_MESSAGE_ARGS=%s,%s", newDateString, oldDateString, + NULL); + } + + // During the boot time, systemd-timesyncd.service checks + // "/var/lib/systemd/timesync/clock" and updates the system time with + // the timestamp of the file + if (!updateClockFileTimestamp()) + { + return false; + } + + return true; + } + + void startSyncTimer(std::shared_ptr<sdbusplus::asio::connection>& conn) + { + // retry 10 times (10 * 5s = 50s ) to get the pch timer + // configuration. + static uint8_t retrytimes = 10; + if (!getPCHI2cAddrFlag) + { + if (retrytimes == 0) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Get pch timer configuration fail"); + return; + } + syncIntervalMS = syncIntervalBootMS; + getPCHTimerConfiguration(conn); + retrytimes--; + } + else + { + if (updateBMCTime()) + { + syncIntervalMS = syncIntervalNormalMS; + } + else + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Update BMC time Fail"); + syncIntervalMS = syncIntervalFastMS; + } + } + + syncTimer->expires_after(std::chrono::milliseconds(syncIntervalMS)); + syncTimer->async_wait( + [this, &conn](const boost::system::error_code& ec) { + if (ec) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Timer cancelled", + phosphor::logging::entry("ECVALUE=%x", ec.value()), + phosphor::logging::entry("ECMESSAGE=%s", + ec.message().c_str())); + return; + } + startSyncTimer(conn); + }); + } + + std::unique_ptr<boost::asio::steady_timer> syncTimer; + uint8_t year, month, day, hour, minute, second; + + public: + PCHSync(boost::asio::io_service& io, + std::shared_ptr<sdbusplus::asio::connection>& conn) + { + syncTimer = std::make_unique<boost::asio::steady_timer>(io); + startSyncTimer(conn); + } + + ~PCHSync() = default; +}; + +int main(int argc, char** argv) +{ + boost::asio::io_service io; + std::shared_ptr<sdbusplus::asio::connection> conn = + std::make_shared<sdbusplus::asio::connection>(io); + sdbusplus::asio::object_server server = + sdbusplus::asio::object_server(conn); + PCHSync pchSyncer(io, conn); + + phosphor::logging::log<phosphor::logging::level::INFO>( + "Starting PCH time sync service"); + + io.run(); + return 0; +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.service b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.service new file mode 100644 index 000000000..bf4e2a30e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.service @@ -0,0 +1,15 @@ +[Unit] +Description=PCH BMC time sync service +Conflicts=systemd-timesyncd.service +Requires=xyz.openbmc_project.EntityManager.service +After=xyz.openbmc_project.EntityManager.service + +[Service] +Restart=always +RestartSec=10 +ExecStart=/usr/bin/pch-time-sync +StartLimitInterval=0 +Type=simple + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/phosphor-time-manager_git.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/phosphor-time-manager_git.bbappend new file mode 100644 index 000000000..4d6031b03 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/phosphor-time-manager_git.bbappend @@ -0,0 +1,2 @@ +RDEPENDS:${PN}:remove = "phosphor-settings-manager" +RDEPENDS:${PN} += " settings" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/0027-Apply-Options-interface-for-Software.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/0027-Apply-Options-interface-for-Software.patch new file mode 100644 index 000000000..3d2cc43e2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/0027-Apply-Options-interface-for-Software.patch @@ -0,0 +1,47 @@ +From 153b125043c28f933579330727d82658979caef3 Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@intel.com> +Date: Mon, 18 May 2020 20:16:04 +0530 +Subject: [PATCH] Apply Options interface for Software + +Apply Options interface is used to pass the settings related to +firmware image activation such as ClearConfig. +ClearConfig property is used to denote whether to clear firmware +configurations along with firmware image activation or not. + +Example case: +BIOS usually requires its NVRAM configuration to be updated or +erased for certain BIOS firmware updates. Current implementation +doesn't support to take this option and provide interface to do +the necessary actions. + +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> +--- + .../Software/ApplyOptions.interface.yaml | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + create mode 100644 xyz/openbmc_project/Software/ApplyOptions.interface.yaml + +diff --git a/xyz/openbmc_project/Software/ApplyOptions.interface.yaml b/xyz/openbmc_project/Software/ApplyOptions.interface.yaml +new file mode 100644 +index 0000000..45a68ab +--- /dev/null ++++ b/xyz/openbmc_project/Software/ApplyOptions.interface.yaml +@@ -0,0 +1,16 @@ ++description: > ++ To implement the Activation apply options for a newly uploaded firmware ++ image. The apply options property is global and is specifically used with ++ each firmware update and is used during firmware activation. ++ ApplyOptions usage during firmware activation is implementation specific, ++ not all firmware targets need ApplyOptions. ++ The default value of this property is false. ++properties: ++ - name: ClearConfig ++ type: boolean ++ default: false ++ description: > ++ This property indicates whether to clear the software configurations ++ when the firmware image update is getting applied. A value of true ++ indicates the firmware configurations should be cleared along with ++ firmware image activation. +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0007-ipmi-set-BIOS-id.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0007-ipmi-set-BIOS-id.patch new file mode 100644 index 000000000..2c443726d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0007-ipmi-set-BIOS-id.patch @@ -0,0 +1,32 @@ +From bc282f4e9537b115e32733dfc6e16c303f81b3e3 Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Tue, 24 Jul 2018 11:40:49 +0800 +Subject: [PATCH] set BIOS id + +change#2 +add new dbus interface for BIOS attributes + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + .../openbmc_project/Inventory/Item/Bios.interface.yaml | 9 +++++++++ + 1 file changed, 9 insertions(+) + create mode 100644 yaml/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml + +diff --git a/yaml/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml b/yaml/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml +new file mode 100644 +index 000000000000..d7a6b95bfdce +--- /dev/null ++++ b/yaml/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml +@@ -0,0 +1,9 @@ ++description: > ++ Implement to provide BIOS attributes. ++properties: ++ - name: BiosId ++ type: string ++ description: > ++ BIOS ID (version) string ++ ++# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0010-Increase-the-default-watchdog-timeout-value.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0010-Increase-the-default-watchdog-timeout-value.patch new file mode 100644 index 000000000..7f315db86 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0010-Increase-the-default-watchdog-timeout-value.patch @@ -0,0 +1,34 @@ +From 479b46d2ed42b576cf2aee2ecbd76de7940d0cfe Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Fri, 10 Aug 2018 16:23:13 +0800 +Subject: [PATCH] Increase the default watchdog timeout value + +The default timeout for poweron is 30 seconds, +but currently the host power on needs 120+ seconds +due to unimplemented ipmi commands for BIOS. + +Increase the value as a workaround, +to avoid the watchdog timeout during power on. +Will adjust this value in the future + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + yaml/xyz/openbmc_project/State/Watchdog.interface.yaml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml b/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml +index f54ec3b6e4fb..ede961da1942 100644 +--- a/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml ++++ b/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml +@@ -37,7 +37,7 @@ properties: + type: uint64 + description: > + Time interval to arm the watchdog, in milli-second. +- default: 30000 ++ default: 600000 + - name: TimeRemaining + type: uint64 + description: > +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0012-Add-RestoreDelay-interface-for-power-restore-delay.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0012-Add-RestoreDelay-interface-for-power-restore-delay.patch new file mode 100644 index 000000000..aae1e0248 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0012-Add-RestoreDelay-interface-for-power-restore-delay.patch @@ -0,0 +1,34 @@ +From 6e8b18e2258c7b6327a9b26901846088dd82a663 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Fri, 24 Aug 2018 17:55:35 +0800 +Subject: [PATCH] Add RestoreDelay interface for power restore delay + +Which provide one property "PowerRestoreDelay" + +Change-Id: I4e6d3e45948b1e288301b4aa52cc08cace4f1bc2 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + .../Control/Power/RestoreDelay.interface.yaml | 11 +++++++++++ + 1 file changed, 11 insertions(+) + create mode 100644 yaml/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml + +diff --git a/yaml/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml b/yaml/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml +new file mode 100644 +index 000000000000..55ee80a75f7b +--- /dev/null ++++ b/yaml/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml +@@ -0,0 +1,11 @@ ++description: > ++ Implement to specify power transition behavior on a BMC reset. ++ The implementation based on restore policy and set a delay time ++ for power restore. ++ ++properties: ++ - name: PowerRestoreDelay ++ type: uint16 ++ description: > ++ The delay time for power restore. ++ Power Restore Delay is NOT applied on power policy is "Always Off" +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch new file mode 100644 index 000000000..17aef45bf --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch @@ -0,0 +1,86 @@ +From 571ab872be4b486b98bfbed159630b5e21d9a586 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Thu, 30 Aug 2018 16:22:43 +0800 +Subject: [PATCH] Add ErrConfig.yaml interface for processor error + configuration. + +Which provide 3 properties: + ResetCfg + type: byte + description: > + Reset Configuration + [0]: CATERR Reset Enabled + 0b: Disabled + 1b: Enabled + [1]: ERR2 Reset Enabled + 0b: Disabled + 1b: Enabled + [7:2]: Reserved + ResetErrorOccurrenceCounts + type: byte + description: > + Reset Error Occurrence Counts + [0]: Reset CPU Error Counts + 0b: Keep CPU Error Counts + 1b: Reset all CPU Error Counts to zero + [7:1]: Reserved + CATERRStatus + type: array[byte] + description: > + For all CPUs including the non-legacy socket CPU + CPU CATERR (Core Error) occurrence + [5:0]: Error Occurrence Count + [7:6]: CPU Status + 00b: Disabled + 01b: Enabled + 11b: Not Present + +Change-Id: Ibc5a7a5e15c998e56c04e23b1043d99243a91171 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + .../Processor/ErrConfig.interface.yaml | 33 +++++++++++++++++++ + 1 file changed, 33 insertions(+) + create mode 100644 yaml/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml + +diff --git a/yaml/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml b/yaml/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml +new file mode 100644 +index 000000000000..23042633ca13 +--- /dev/null ++++ b/yaml/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml +@@ -0,0 +1,33 @@ ++description: > ++ This defines processor error configuration. ++properties: ++ - name: ResetCfg ++ type: byte ++ description: > ++ Reset Configuration ++ [0]: CATERR Reset Enabled ++ 0b: Disabled ++ 1b: Enabled ++ [1]: ERR2 Reset Enabled ++ 0b: Disabled ++ 1b: Enabled ++ [7:2]: Reserved ++ ++ - name: ResetErrorOccurrenceCounts ++ type: byte ++ description: > ++ Reset Error Occurrence Counts ++ [0]: Reset CPU Error Counts ++ 0b: Keep CPU Error Counts ++ 1b: Reset all CPU Error Counts to zero ++ [7:1]: Reserved ++ - name: CATERRStatus ++ type: array[byte] ++ description: > ++ For all CPUs including the non-legacy socket CPU ++ CPU CATERR (Core Error) occurrence ++ [5:0]: Error Occurrence Count ++ [7:6]: CPU Status ++ 00b: Disabled ++ 01b: Enabled ++ 11b: Not Present +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0024-Add-the-pre-timeout-interrupt-defined-in-IPMI-spec.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0024-Add-the-pre-timeout-interrupt-defined-in-IPMI-spec.patch new file mode 100644 index 000000000..06eed126f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0024-Add-the-pre-timeout-interrupt-defined-in-IPMI-spec.patch @@ -0,0 +1,57 @@ +From 45f4457c23a8da9d246bd33c3d426b52186b39f0 Mon Sep 17 00:00:00 2001 +From: Ren Yu <yux.ren@intel.com> +Date: Fri, 24 May 2019 14:55:10 +0800 +Subject: [PATCH] Add the pre-timeout interrupt defined in IPMI spec + +The IPMI watchdog pre-timeout interrupt is used to set the different +pre-timeout interrupt source. Add them as a dbus property, +IPMI set/get watchdog commands will use it. + +Signed-off-by: Ren Yu <yux.ren@intel.com> +--- + .../State/Watchdog.interface.yaml | 22 +++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml b/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml +index ede961da1942..a67a6a30ea2c 100644 +--- a/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml ++++ b/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml +@@ -33,6 +33,11 @@ properties: + description: > + The action the watchdog should perform when it expires. + default: 'HardReset' ++ - name: PreTimeoutInterrupt ++ type: enum[self.PreTimeoutInterruptAction] ++ description: > ++ The BMC generates the selected interrupt before the timer expires. ++ default: 'None' + - name: Interval + type: uint64 + description: > +@@ -73,6 +78,23 @@ enumerations: + description: > + Perform a power cycle of the system. + ++ - name: PreTimeoutInterruptAction ++ description: > ++ The type of PreTimeout Interrupt. ++ values: ++ - name: 'None' ++ description: > ++ Do nothing. ++ - name: 'SMI' ++ description: > ++ SMI. ++ - name: 'NMI' ++ description: > ++ NMI / Diagnostic Interrupt. ++ - name: 'MI' ++ description: > ++ Messaging Interrupt. ++ + - name: TimerUse + description: > + The type of timer use. +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0025-Add-PreInterruptFlag-properity-in-DBUS.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0025-Add-PreInterruptFlag-properity-in-DBUS.patch new file mode 100644 index 000000000..54920fee3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0025-Add-PreInterruptFlag-properity-in-DBUS.patch @@ -0,0 +1,39 @@ +From c71c17951e5f00195c9cacd6829a359c3d253714 Mon Sep 17 00:00:00 2001 +From: Ren Yu <yux.ren@intel.com> +Date: Mon, 29 Jul 2019 10:51:12 +0800 +Subject: [PATCH] Add PreInterruptFlag properity in DBUS. + +PreTimeoutInterruptOccurFlag in DBUS would be set 'true' +when watchdog pre-timeout interrupt occurred. + +Tested: +Enable command(raw 0x06 0x31) that get message flag +can set right bit about watchdog, +need record PreTimeoutInterruptOccurFlag +at xyz.openbmmc_project.State.Watchdog when watchdog +pre-timeout interrupt occurred. + +Signed-off-by: Ren Yu <yux.ren@intel.com> +--- + yaml/xyz/openbmc_project/State/Watchdog.interface.yaml | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml b/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml +index a67a6a30ea2c..d1ae8e3411e6 100644 +--- a/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml ++++ b/yaml/xyz/openbmc_project/State/Watchdog.interface.yaml +@@ -59,6 +59,11 @@ properties: + description: > + The timer user at the time of expiration. + default: 'Reserved' ++ - name: PreTimeoutInterruptOccurFlag ++ type: boolean ++ description: > ++ PreTimeoutInterruptOccurFlag that preTimeoutInterrupt action occurred. ++ default: false + + enumerations: + - name: Action +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0026-Add-StandbySpare-support-for-software-inventory.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0026-Add-StandbySpare-support-for-software-inventory.patch new file mode 100644 index 000000000..25ed9a403 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0026-Add-StandbySpare-support-for-software-inventory.patch @@ -0,0 +1,55 @@ +From 826946ecad67b34818db0634367c4cf7100d0890 Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@intel.com> +Date: Wed, 15 Jan 2020 10:01:04 +0800 +Subject: [PATCH] Add StandbySpare support for software inventory + +Add support to allow update for active / recovery +regions of specified firmware. This update enables +the backend modules to advertise whether the +software object is active or recovery (StandbySpare) +image. + +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> +--- + .../Software/Activation.interface.yaml | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/yaml/xyz/openbmc_project/Software/Activation.interface.yaml b/yaml/xyz/openbmc_project/Software/Activation.interface.yaml +index 6af2984b7b2e..26ab87b0262d 100644 +--- a/yaml/xyz/openbmc_project/Software/Activation.interface.yaml ++++ b/yaml/xyz/openbmc_project/Software/Activation.interface.yaml +@@ -28,12 +28,20 @@ enumerations: + - name: Activating + description: > + The Software.Version is in the process of being Activated. ++ - name: ActivatingAsStandbySpare ++ description: > ++ The Software.Version is in the process of being processed ++ as StandbySpare. + - name: Active + description: > + The Software.Version is currently Active. + - name: Failed + description: > + The Software.Version failed during or after Activation. ++ - name: StandbySpare ++ description: > ++ The Software.Version is part of a redundancy set and awaits ++ a failover or external action to activate. + - name: Staged + description: > + The Software.Version is currently in staged flash area. +@@ -52,6 +60,10 @@ enumerations: + - name: Active + description: > + The Software.Version has been requested for Activation. ++ - name: StandbySpare ++ description: > ++ The Software.Version has been requested to be enabled as ++ StandbySpare. + # TODO: Specify "EAGAIN" type error when requested is unable to be acted on + # due to current system state. Currently, sdbusplus does not support + # errors on properties. +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0028-MCTP-Daemon-D-Bus-interface-definition.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0028-MCTP-Daemon-D-Bus-interface-definition.patch new file mode 100644 index 000000000..677b699be --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0028-MCTP-Daemon-D-Bus-interface-definition.patch @@ -0,0 +1,401 @@ +From 2c270f8fd9f45ff0792d2730c25eb2f673314115 Mon Sep 17 00:00:00 2001 +From: "Kowalski, Mariusz" <mariusz.kowalski@intel.com> +Date: Thu, 27 Feb 2020 15:48:56 +0100 +Subject: [PATCH] MCTP Daemon D-Bus interface definition. + +This interface definition was created on base of the MCTP design +proposed in this document: +https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/28424/9/designs/mctp.md + +Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com> +Signed-off-by: Mariusz Kowalski <mariusz.kowalski@intel.com> +Signed-off-by: Karol Wachowski <karol.wachowski@intel.com> +Change-Id: Ida66f8ffcf00003655edcb0fb0112202797b8e1a +--- + .../openbmc_project/MCTP/Base.interface.yaml | 227 ++++++++++++++++++ + .../MCTP/Binding/PCIe.interface.yaml | 29 +++ + .../MCTP/Binding/SMBus.interface.yaml | 17 ++ + .../MCTP/BusOwner.interface.yaml | 17 ++ + .../MCTP/Endpoint.interface.yaml | 4 + + .../MCTP/SupportedMessageTypes.interface.yaml | 36 +++ + 6 files changed, 330 insertions(+) + create mode 100644 yaml/xyz/openbmc_project/MCTP/Base.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/MCTP/Binding/PCIe.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/MCTP/Binding/SMBus.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/MCTP/BusOwner.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/MCTP/SupportedMessageTypes.interface.yaml + +diff --git a/yaml/xyz/openbmc_project/MCTP/Base.interface.yaml b/yaml/xyz/openbmc_project/MCTP/Base.interface.yaml +new file mode 100644 +index 000000000000..9438551e648e +--- /dev/null ++++ b/yaml/xyz/openbmc_project/MCTP/Base.interface.yaml +@@ -0,0 +1,227 @@ ++description: > ++ Mandatory interface for each instance of the MCTP Daemon to expose ++ the base MCTP daemon and medium type interfaces. ++ ++methods: ++ - name: SendMctpMessagePayload ++ description: > ++ Sends message over MCTP interface ++ parameters: ++ - name: DestinationEID ++ type: byte ++ description: > ++ Destination Endpoint ID. The logical address used to route MCTP ++ messages to a specific MCTP endpoint. ++ - name: MsgTag ++ type: byte ++ description: > ++ Message tag. Field that, along with the Source Endpoint IDs and the ++ Tag Owner (TO) field, identifies a unique message at the MCTP ++ transport level. ++ - name: TagOwner ++ type: boolean ++ description: > ++ Tag Owner bit identifies whether the message tag was originated by ++ the endpoint that is the source of the message or by the endpoint ++ that is the destination of the message. ++ - name: Payload ++ type: array[byte] ++ description: Payload of message. ++ returns: ++ - name: Status ++ type: byte ++ description: 0 - if success ++ errors: ++ - xyz.openbmc_project.Common.Error.Timeout ++ - xyz.openbmc_project.Common.Error.InvalidArgument ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ ++ - name: SendMctpMessageFileDescriptor ++ description: > ++ Sends message over MCTP interface ++ parameters: ++ - name: DestinationEID ++ type: byte ++ description: > ++ Destination Endpoint ID. The logical address used to route MCTP ++ messages to a specific MCTP endpoint. ++ - name: MsgTag ++ type: byte ++ description: > ++ Message tag. Field that, along with the Source Endpoint IDs and the ++ Tag Owner (TO) field, identifies a unique message at the MCTP ++ transport level. ++ - name: TagOwner ++ type: boolean ++ description: > ++ Tag Owner bit identifies whether the message tag was originated by ++ the endpoint that is the source of the message or by the endpoint ++ that is the destination of the message. ++ - name: FileDescriptor ++ type: unixfd ++ description: File descriptor of message. ++ returns: ++ - name: Status ++ type: byte ++ description: 0 - if success ++ errors: ++ - xyz.openbmc_project.Common.Error.Timeout ++ - xyz.openbmc_project.Common.Error.InvalidArgument ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ ++signals: ++ - name: MessageReceivedSignal ++ description: > ++ Signal indicating upper layers about arrival of a MCTP message. ++ properties: ++ - name: MessageType ++ type: enum[self.MessageTypes] ++ description: > ++ Defines the values for the Message Type field for different message ++ types transported through MCTP. ++ - name: SrcEid ++ type: byte ++ description: > ++ Source Endpoint ID. The logical address used to route MCTP messages ++ to a specific MCTP endpoint. ++ - name: MsgTag ++ type: byte ++ description: > ++ Message tag. Field that, along with the Source Endpoint IDs and the ++ Tag Owner (TO) field, identifies a unique message at the MCTP ++ transport level. ++ - name: TagOwner ++ type: boolean ++ description: > ++ Tag Owner bit identifies whether the message tag was originated by ++ the endpoint that is the source of the message or by the endpoint ++ that is the destination of the message. ++ - name: Payload ++ type: array[byte] ++ description: Payload of message. ++ ++properties: ++ - name: Eid ++ type: byte ++ description: > ++ Endpoint ID. The logical address used to route MCTP messages to a ++ specific MCTP endpoint. ++ ++ - name: BindingID ++ type: enum[self.BindingTypes] ++ ++ - name: BindingMediumID ++ type: enum[self.MctpPhysicalMediumIdentifiers] ++ ++ - name: StaticEid ++ type: boolean ++ description: Support for statically/dynamicly allocated IDs ++ ++ - name: BindingMode ++ type: enum[self.BindingModeTypes] ++ description: Bus Owner / Endpoint / Bridge ++ ++enumerations: ++ - name: BindingTypes ++ description: > ++ All other values than described are reserved. ++ values: ++ - name: MctpOverSmbus ++ - name: MctpOverPcieVdm ++ - name: MctpOverUsb ++ description: Reserved for MCTP over USB ++ - name: MctpOverKcs ++ - name: MctpOverSerial ++ - name: VendorDefined ++ ++ - name: MctpPhysicalMediumIdentifiers ++ description: > ++ Identifies MCTP physical medium identifiers. see DSP0239. ++ values: ++ - name: Smbus ++ descritpion: SMBus 2.0 100 kHz compatible ++ - name: SmbusI2c ++ descritpion: SMBus 2.0 + I2C 100 kHz compatible ++ - name: I2cCompatible ++ description: I2C 100 kHz compatible (Standard-mode) ++ - name: Smbus3OrI2c400khzCompatible ++ description: SMBus 3.0 or I2C 400 kHz compatible (Fast-mode) ++ - name: Smbus3OrI2c1MhzCompatible ++ description: SMBus 3.0 or I2C 1 MHz compatible (Fast-mode Plus) ++ - name: I2c3Mhz4Compatible ++ description: I2C 3.4 MHz compatible (High-speed mode) ++ - name: Pcie11 ++ description: PCIe revision 1.1 compatible ++ - name: Pcie2 ++ description: PCIe revision 2.0 compatible ++ - name: Pcie21 ++ description: PCIe revision 2.1 compatible ++ - name: Pcie3 ++ description: PCIe revision 3.0 compatible ++ - name: Pcie4 ++ description: PCIe revision 4.0 compatible ++ - name: Pcie5 ++ description: PCIe revision 4.0 compatible ++ - name: PciCompatible ++ description: > ++ PCI compatible (PCI 1.0,2.0,2.1,2.2,2.3,3.0,PCI-X 1.0, PCI-X 2.0) ++ - name: Usb11Compatible ++ description: USB 1.1 compatible ++ - name: Usb20Compatible ++ description: USB 2.0 compatible ++ - name: Usb30Compatible ++ description: USB 3.0 compatible ++ - name: NcSiOverRbt ++ description: > ++ NC-SI over RBT (A physical interface based on RMII as defined in ++ DSP0222) ++ - name: KcsLegacy ++ description: KCS1 / Legacy (Fixed Address Decoding) ++ - name: KcsPci ++ description: KCS1 / PCI (Base Class 0xC0 Subclass 0x01) ++ - name: SerialHostLegacy ++ description: Serial Host2 / Legacy (Fixed Address Decoding) ++ - name: SerialHostPci ++ description: Serial Host2 / PCI (Base Class 0x07 Subclass 0x00) ++ - name: AsynchronousSerial ++ description: Asynchronous Serial (Between MCs and IMDs) ++ - name: I3cSDR ++ description: I3C 12.5 MHz compatible (SDR) ++ - name: I3cHDRDDR ++ description: I3C 25 MHz compatible (HDR-DDR) ++ ++ - name: BindingModeTypes ++ values: ++ - name: Endpoint ++ description: > ++ An MCTP communication terminus. An MCTP endpoint is a terminus or ++ origin of MCTP packets or messages. That is, the combined ++ functionality within a physical device that communicates using the ++ MCTP transport protocol and handles MCTP control commands. This ++ includes MCTP-capable management controllers and managed devices. ++ Also referred to in this document as "endpoint". ++ - name: BusOwner ++ description: > ++ The party responsible for managing address assignments (can be ++ logical or physical addresses) on a bus (for example, in MCTP, the ++ bus owner is the party responsible for managing EID assignments for ++ a given bus). A bus owner may also have additional media-specific ++ responsibilities, such as assignment of physical addresses. ++ - name: Bridge ++ description: > ++ An MCTP endpoint that can route MCTP messages not destined for ++ itself that it receives on one interconnect onto another without ++ interpreting them. The ingress and egress media at the bridge may ++ be either homogeneous or heterogeneous. Also referred to in this ++ document as a "bridge". ++ ++ - name: MessageTypes ++ values: ++ - name: MctpControl ++ - name: PLDM ++ - name: NCSI ++ - name: Ethernet ++ - name: NVMeMgmtMsg ++ - name: SPDM ++ - name: VDPCI ++ - name: VDIANA +diff --git a/yaml/xyz/openbmc_project/MCTP/Binding/PCIe.interface.yaml b/yaml/xyz/openbmc_project/MCTP/Binding/PCIe.interface.yaml +new file mode 100644 +index 000000000000..1bd28818b39a +--- /dev/null ++++ b/yaml/xyz/openbmc_project/MCTP/Binding/PCIe.interface.yaml +@@ -0,0 +1,29 @@ ++description: > ++ Interface exposed by MCTP daemon for PCIe binding ++ ++properties: ++ - name: DiscoveredFlag ++ type: enum[self.DiscoveryFlags] ++ description: > ++ Each endpoint (except the bus owner) on the PCIe bus maintains an ++ internal flag called the Discovered flag. The flag is set to the ++ discovered state when the Set Endpoint ID command is received. ++ ++ - name: BDF ++ type: uint16 ++ description: > ++ Byte 1 [7:0] Bus number ++ Byte 2 [7:3] Device number [2:0] Function Number ++ ++enumerations: ++ - name: DiscoveryFlags ++ description: > ++ The Prepare for Endpoint Discovery message causes each recipient ++ endpoint on the PCIe bus to set their respective Discovered flag to ++ the undiscovered state. For the Prepare for Endpoint Discovery request ++ message, the routing in the physical transport header should be set to ++ 011b (Broadcast from Root Complex). ++ values: ++ - name: Discovered ++ - name: Undiscovered ++ - name: NotApplicable +diff --git a/yaml/xyz/openbmc_project/MCTP/Binding/SMBus.interface.yaml b/yaml/xyz/openbmc_project/MCTP/Binding/SMBus.interface.yaml +new file mode 100644 +index 000000000000..9219ad02af06 +--- /dev/null ++++ b/yaml/xyz/openbmc_project/MCTP/Binding/SMBus.interface.yaml +@@ -0,0 +1,17 @@ ++description: > ++ Interface exposed by MCTP daemon for SMBus binding ++ ++properties: ++ - name: ArpMasterSupport ++ type: boolean ++ description: > ++ The SMBus binding can also run ARP Master protocol and ++ assign SMBus addresses to the devices on the bus. ++ ++ - name: BusNumber ++ type: byte ++ description: I2C bus number of the medium used ++ ++ - name: SlaveAddress ++ type: byte ++ description: Slave address to be used for this medium +diff --git a/yaml/xyz/openbmc_project/MCTP/BusOwner.interface.yaml b/yaml/xyz/openbmc_project/MCTP/BusOwner.interface.yaml +new file mode 100644 +index 000000000000..d46298ed35b4 +--- /dev/null ++++ b/yaml/xyz/openbmc_project/MCTP/BusOwner.interface.yaml +@@ -0,0 +1,17 @@ ++description: > ++ Interface exposed by MCTP root object, when executing in Bus Owner mode. ++ ++properties: ++ - name: EidPool ++ type: array[struct[byte, byte]] ++ description: > ++ Pool of allowed EIDs to be used. ++ EID pool of 10-100 can be specified as {{10,100}}. ++ ++ - name: TopMostBusOwner ++ type: boolean ++ description: To indicate whether BMC is topmost Bus Owner ++ ++ - name: OwnEidPool ++ type: boolean ++ description: Indicates Eid pool is managed by self +diff --git a/yaml/xyz/openbmc_project/MCTP/Endpoint.interface.yaml b/yaml/xyz/openbmc_project/MCTP/Endpoint.interface.yaml +index 119f1c673c2c..abb3ac930236 100644 +--- a/yaml/xyz/openbmc_project/MCTP/Endpoint.interface.yaml ++++ b/yaml/xyz/openbmc_project/MCTP/Endpoint.interface.yaml +@@ -6,6 +6,10 @@ description: > + MCTP-capable management controllers and managed devices. + + properties: ++ - name: Mode ++ type: enum[xyz.openbmc_project.MCTP.Base.BindingModeTypes] ++ description: > ++ Endpoint / BusOwner / Bridge + + - name: NetworkId + type: size +diff --git a/yaml/xyz/openbmc_project/MCTP/SupportedMessageTypes.interface.yaml b/yaml/xyz/openbmc_project/MCTP/SupportedMessageTypes.interface.yaml +new file mode 100644 +index 000000000000..fa447ee6a3bb +--- /dev/null ++++ b/yaml/xyz/openbmc_project/MCTP/SupportedMessageTypes.interface.yaml +@@ -0,0 +1,36 @@ ++description: ++ Interface used to represent the supported MCTP message types. ++ This will be exposed by all MCTP endpoints. ++ ++properties: ++ - name: MctpControl ++ type: boolean ++ description: Indicates support availability ++ ++ - name: PLDM ++ type: boolean ++ description: Indicates support availability ++ ++ - name: NCSI ++ type: boolean ++ description: Indicates support availability ++ ++ - name: Ethernet ++ type: boolean ++ description: Indicates support availability ++ ++ - name: NVMeMgmtMsg ++ type: boolean ++ description: Indicates support availability ++ ++ - name: SPDM ++ type: boolean ++ description: Indicates support availability ++ ++ - name: VDPCI ++ type: boolean ++ description: Indicates support availability ++ ++ - name: VDIANA ++ type: boolean ++ description: Indicates support availability +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0029-Add-D-Bus-interfaces-for-PLDM-FW-update.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0029-Add-D-Bus-interfaces-for-PLDM-FW-update.patch new file mode 100644 index 000000000..6da1c2693 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0029-Add-D-Bus-interfaces-for-PLDM-FW-update.patch @@ -0,0 +1,494 @@ +From 2da0a05ae9b69cd8da67ced1bcac6ae7c6c5f930 Mon Sep 17 00:00:00 2001 +From: "Gade-RajasekharReddy@" <Gade-RajasekharReddy@> +Date: Wed, 16 Sep 2020 03:19:41 +0530 +Subject: [PATCH] Add D-Bus interfaces for PLDM FW update + +Added PLDM FW update base interface, which exposes a method. Using +this method PLDM FWU can be initiated. + +Added interfaces for exposing PLDM FW update inventory info. + +Test +supporting files are created for the yaml files. + +Signed-off-by: Gade-RajasekharReddy@ <raja.sekhar.reddy.gade@linux.intel.com> +--- + .../PLDM/FWU/ACPIDescriptor.interface.yaml | 14 +++ + ...ActiveComponentImageSetInfo.interface.yaml | 9 ++ + .../FWU/ActiveComponentInfo.interface.yaml | 55 ++++++++++ + .../CapabilitiesDuringUpdate.interface.yaml | 32 ++++++ + .../ComponentActivationMethods.interface.yaml | 40 +++++++ + .../PLDM/FWU/FWUBase.interface.yaml | 21 ++++ + .../PLDM/FWU/IANADescriptor.interface.yaml | 10 ++ + .../PLDM/FWU/PCIDescriptor.interface.yaml | 30 +++++ + ...endingComponentImageSetInfo.interface.yaml | 10 ++ + .../FWU/PendingComponentInfo.interface.yaml | 40 +++++++ + .../PLDM/FWU/PnPDescriptor.interface.yaml | 14 +++ + yaml/xyz/openbmc_project/PLDM/FWU/README.md | 103 ++++++++++++++++++ + 12 files changed, 378 insertions(+) + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/FWUBase.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/IANADescriptor.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/PCIDescriptor.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/PnPDescriptor.interface.yaml + create mode 100644 yaml/xyz/openbmc_project/PLDM/FWU/README.md + +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor.interface.yaml +new file mode 100644 +index 000000000000..e225bade0df0 +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor.interface.yaml +@@ -0,0 +1,14 @@ ++description : > ++ This interface has ACPI descriptor properties. ++ ++properties : ++ ++ - name : ACPIVendorID ++ type : string ++ description: > ++ Property containing ACPI Vendor ID. ++ ++ - name : ACPIProductIdentifier ++ type : string ++ description: > ++ Property containing ACPI Product Identifier. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo.interface.yaml +new file mode 100644 +index 000000000000..94115a33e1d9 +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo.interface.yaml +@@ -0,0 +1,9 @@ ++description : > ++ This interface has the PLDM FWU active component image set properties. ++ ++properties : ++ ++ - name : ActiveComponentImageSetVersionString ++ type : string ++ description: > ++ String describing the active component image set version. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo.interface.yaml +new file mode 100644 +index 000000000000..77a75669439b +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo.interface.yaml +@@ -0,0 +1,55 @@ ++description: > ++ This interface has the component entries for all of the updatable ++ active components that reside on the FD. ++ ++properties: ++ ++ - name: ComponentClassification ++ type: uint16 ++ description: > ++ Vendor specific component classification information. ++ Special values: 0x0000, 0xFFFF are reserved. ++ ++ - name: ComponentIdentifier ++ type: uint16 ++ description: > ++ FD vendor selected unique value to distinguish between ++ component images. ++ ++ - name: ComponentClassificationIndex ++ type: byte ++ description: > ++ Used to distinguish identical components that have the same ++ classification and identifier that can use the same component ++ image but the images are stored in different locations in the FD. ++ ++ - name: ActiveComponentComparisonStamp ++ type: uint32 ++ description: > ++ Optional Firmware component comparison stamp that is currently ++ active. If the firmware component does not provide a component ++ comparison stamp, this value should be set to 0x00000000. ++ ++ - name: ActiveComponentReleaseDate ++ type: string ++ description: > ++ Containing the date corresponding to the component version ++ level being reported – Format YYYYMMDD. ++ If the firmware component does not provide a date, this string ++ shall be empty. ++ ++ - name: ComponentAutoApply ++ type: boolean ++ description: > ++ Firmware Device performs an ‘auto-apply’ during transfer ++ phase and apply step will be completed immediately if this ++ property is true. ++ Firmware Device will execute an operation during the APPLY ++ state that will include migrating the new component image to its ++ final non-volatile storage destination if this property is ++ false. ++ ++ - name: ActiveComponentVersionString ++ type: string ++ description: > ++ String describing the active component version. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate.interface.yaml +new file mode 100644 +index 000000000000..36560ff0742b +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate.interface.yaml +@@ -0,0 +1,32 @@ ++description : > ++ This interface describes the capabilities during update. ++ ++properties : ++ ++ - name : UpdateModeRestrictions ++ type : boolean ++ description: > ++ This property tells whether update mode restrictions are ++ supported or not. ++ ++ - name : PartialUpdates ++ type : boolean ++ description: > ++ This property tells whether partial updates are supported or not. ++ ++ - name : HostFunctionalityDuringFirmwareUpdate ++ type : boolean ++ description: > ++ This property tells whether the host device functionality ++ during firmware update is reduced or not. ++ ++ - name : ComponentUpdateFailureRetryCapability ++ type : boolean ++ description: > ++ This property shows the component update failure retry capability. ++ ++ - name : ComponentUpdateFailureRecoveryCapability ++ type : boolean ++ description: > ++ This property shows the component update failure recovery ++ capability. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods.interface.yaml +new file mode 100644 +index 000000000000..d5ec47cbc77f +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods.interface.yaml +@@ -0,0 +1,40 @@ ++description: > ++ This interface has the component activation methods. ++ ++properties: ++ ++ - name: ACPowerCycle ++ type: boolean ++ description: > ++ Property that tells whether AC power cycle is an activation ++ method or not. ++ ++ - name: DCPowerCycle ++ type: boolean ++ description: > ++ Property that tells whether DC power cycle is an activation ++ method or not. ++ ++ - name: SystemReboot ++ type: boolean ++ description: > ++ Property that tells whether System reboot is an activation ++ method or not. ++ ++ - name: MediumSpecificReset ++ type: boolean ++ description: > ++ Property that tells whether Medium-specific reset is an ++ activation method or not. ++ ++ - name: SelfContained ++ type: boolean ++ description: > ++ Property that tells whether Self-Contained option is activation ++ method or not. ++ ++ - name: Automatic ++ type: boolean ++ description: > ++ Property that tells whether the component can be activated ++ automatically once apply completes. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/FWUBase.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/FWUBase.interface.yaml +new file mode 100644 +index 000000000000..2ba15e26c690 +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/FWUBase.interface.yaml +@@ -0,0 +1,21 @@ ++description: > ++ This interface provides a method to initiate the PLDM FW update. ++ ++methods: ++ - name: StartFWUpdate ++ description: > ++ This method initiates the PLDM FW update. ++ parameters: ++ - name: filePath ++ type: string ++ description: > ++ PLDM FW update package path. ++ returns: ++ - name: status ++ type: byte ++ description: > ++ PLDM FW update status. ++ errors: ++ - xyz.openbmc_project.Common.Error.NotAllowed ++ - xyz.openbmc_project.Common.Error.InvalidArgument ++ - xyz.openbmc_project.Common.Error.ResourceNotFound +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/IANADescriptor.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/IANADescriptor.interface.yaml +new file mode 100644 +index 000000000000..c013955af3b6 +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/IANADescriptor.interface.yaml +@@ -0,0 +1,10 @@ ++description : > ++ This interface has device identification info, in which IANA ++ Enterprise ID is used as descriptor. ++ ++properties : ++ ++ - name : IANAEnterpriseID ++ type : string ++ description: > ++ Property containing the IANA Enterprise ID. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/PCIDescriptor.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/PCIDescriptor.interface.yaml +new file mode 100644 +index 000000000000..8d758ed51192 +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/PCIDescriptor.interface.yaml +@@ -0,0 +1,30 @@ ++description : > ++ This interface has device identification info,in which PCI Vendor ID ++ is used as descriptor. ++ ++properties : ++ ++ - name : PCIVendorID ++ type : string ++ description: > ++ Property containing the PCI Vendor ID. ++ ++ - name : PCIDeviceID ++ type : string ++ description: > ++ Property containing the PCI Device ID. ++ ++ - name : PCISubsystemVendorID ++ type : string ++ description: > ++ Property containing the PCI Subsystem Vendor ID. ++ ++ - name : PCISubsystemID ++ type : string ++ description: > ++ Property containing the PCI Subsystem ID. ++ ++ - name : PCIRevisionID ++ type : string ++ description: > ++ Property containing the PCI Revision ID. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo.interface.yaml +new file mode 100644 +index 000000000000..3861572d81a4 +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo.interface.yaml +@@ -0,0 +1,10 @@ ++description : > ++ This interface has the PLDM FWU pending component image set ++ properties. ++ ++properties : ++ ++ - name : PendingComponentImageSetVersionString ++ type : string ++ description: > ++ String describing the pending component image set version. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo.interface.yaml +new file mode 100644 +index 000000000000..59a2ad8dae8c +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo.interface.yaml +@@ -0,0 +1,40 @@ ++description: > ++ This interface has the component entries for all of the pending ++ components that reside on the FD. ++ ++properties: ++ ++ - name: PendingComponentComparisonStamp ++ type: uint32 ++ description: > ++ Optional firmware component comparison stamp that is pending ++ activation. This field, and all other pending component fields, ++ are valid once the firmware device has received the ++ ActivateFirmware command to prepare the firmware component for ++ activation, but the activation method requires further action ++ to enable the pending image to become the actively running code ++ image. ++ If no pending firmware component exists, this value shall be ++ set to 0x00000000 ++ ++ - name: PendingComponentReleaseDate ++ type: string ++ description: > ++ Eight byte field containing the date corresponding to the ++ component version level being reported – Format YYYYMMDD. ++ If no pending firmware component exists, this string ++ shall be empty. ++ ++ ++ - name: PendingComponentVersionString ++ type: string ++ description: > ++ Firmware component version, which is pending activation. The ++ version reported here should be the one that will become active ++ on the next initialization or activation of the component. The ++ pending component version value may be same as the active ++ component version. Contains a variable type string describing ++ the pending component version. Refer to ++ PendingComponentComparisonStamp field for additional details. ++ If no pending firmware component exists, this field is zero ++ bytes in length. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/PnPDescriptor.interface.yaml b/yaml/xyz/openbmc_project/PLDM/FWU/PnPDescriptor.interface.yaml +new file mode 100644 +index 000000000000..801db6d6380c +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/PnPDescriptor.interface.yaml +@@ -0,0 +1,14 @@ ++description : > ++ This interface has PnP descriptor properties. ++ ++properties : ++ ++ - name : PnPVendorID ++ type : string ++ description: > ++ Property containing the PnP Vendor ID. ++ ++ - name : PnPProductIdentifier ++ type : string ++ description: > ++ Property containing the PnP Product Identifier. +diff --git a/yaml/xyz/openbmc_project/PLDM/FWU/README.md b/yaml/xyz/openbmc_project/PLDM/FWU/README.md +new file mode 100644 +index 000000000000..293173933baf +--- /dev/null ++++ b/yaml/xyz/openbmc_project/PLDM/FWU/README.md +@@ -0,0 +1,103 @@ ++#PLDM FW Update ++ ++##Overview ++ ++The PLDM FW update package contains two major sections: the FW package ++header, and the FW package payload. The FW package header is required to ++describe the target device that the package is intended to update and ++the component images that the FW update package contains. The FW ++package payload is the actual FW image which can be used by FW device ++for FW update. ++ ++Update Agent(BMC) will send the inventory commands to the all the ++devices which are capable of PLDM FW update and exposes the inventory ++info to the D-Bus. How PLDM FW update package reaches BMC is out of ++scope of PLDM FWU spec 1.0.1. Once BMC receives the FW package, it ++matches the package header with the inventory info, if matches proceeds ++for FW update. ++ ++###PLDM FW update interfaces overview and hierarchy ++ ++/xyz/openbmc_project/pldm/fwu ++|--xyz.openbmc_project.PLDM.FWU.FWUBase ++| ++|__/xyz/openbmc_project/pldm/fwu/<tid> ++ | ++ |__/xyz/openbmc_project/pldm/fwu/<tid>/deviceDescriptors ++ | |--xyz.openbmc_project.PLDM.FWU.PCIDescriptor ++ | |--xyz.openbmc_project.PLDM.FWU.IANADescriptor ++ | |--xyz.openbmc_project.PLDM.FWU.PnPDescriptor ++ | |--xyz.openbmc_project.PLDM.FWU.ACPIDescriptor ++ | ++ |__/xyz/openbmc_project/pldm/fwu/<tid>/componentImageSetInfo ++ | |--xyz.openbmc_project.PLDM.FWU.ActiveComponentImageSetInfo ++ | |--xyz.openbmc_project.PLDM.FWU.PendingComponentImageSetInfo ++ | ++ |__/xyz/openbmc_project/pldm/fwu/<tid>/componentImageSetInfo/component_<component_no> ++ |--xyz.openbmc_project.PLDM.FWU.ActiveComponentInfo ++ |--xyz.openbmc_project.PLDM.FWU.PendingComponentInfo ++ |--xyz.openbmc_project.PLDM.FWU.ComponentActivationMethods ++ |--xyz.openbmc_project.PLDM.FWU.CapabilitiesDuringUpdate ++ ++Note: ++Descriptor for a device shall be defined by one of the following ++(PCI Vendor ID, IANA Enterprise ID, UUID, PnP Vendor ID, or ACPI Vendor ++ID) and the corresponding descriptor`s interface is exposed by the. ++Device Descriptors object. ++No new UUID descriptor incterface is defined as the existing UUID ++interface will be used. ++ ++####FW Update Base ++It is exposed by the object `/xyz/openbmc_project/pldm/fwu` with the ++following interface ++1. `xyz.openbmc_project.pldm.FWUBase` exposes a method by which PLDM ++FWU can be initiated. ++ ++Each FW update capable device info is exposed by the object ++`/xyz/openbmc_project/pldm/fwu/<tid>`. ++It will have the following objects. ++1. Device Descriptors ++2. Component Image Set Info ++3. Component Image Info (Each component is exposed as an object) ++ ++####Device Descriptors ++Device Descriptors are exposed under the object path ++`/xyz/openbmc_project/pldm/fwu/deviceDescriptors` with one of the ++following interfaces. ++1. `xyz.openbmc_project.PLDM.FWU.PCIDescriptor` which exposes the PCI ++device descriptors. If the FD is a PCI device then this interface will ++be exposed by the device descriptors object. ++2. `xyz.openbmc_project.PLDM.FWU.IANADescriptor` which exposes IANA ++descriptor properties. If FD have IANA Enterprise ID as the descriptor ++type then this interface will be exposed by the device descriptors ++object. ++3. `xyz.openbmc_project.PLDM.FWU.PnPDescriptor` which exposes the Pnp ++descriptor properties. If FD have PnP vendor ID as the descriptor ++type then this interface will be exposed by the device descriptors ++object. ++4. `xyz.openbmc_project.PLDM.FWU.ACPIDescriptor` which exposes the ACPI ++descriptor properties. If FD have ACPI vendor ID as the descriptor ++type then this interface will be exposed by the device descriptors ++object. ++ ++####Component Image Set Info ++Component Image Set Info is exposed under the object path ++`/xyz/openbmc_project/pldm/fwu/componentImageSetInfo` with the ++following interface. ++1. `xyz.openbmc_project.PLDM.FWU.ActiveComponentImageSetInfo` which ++exposes the active component image set properties. ++2. `xyz.openbmc_project.PLDM.FWU.PendingComponentImageSetInfo` which ++exposes the pending component image set properties. ++ ++####Component Image Info ++Component Image Info is exposed under the object path ++`/xyz/openbmc_project/pldm/fwu/componentImageSetInfo/componentInfo_<component_no>' ++with the following interface ++1. `xyz.openbmc_project.PLDM.FWU.ActiveComponentInfo` which exposes the ++component Image properties. ++2. `xyz.openbmc_project.PLDM.FWU.PendingComponentInfo` which exposes the ++component Image properties. ++3. `xyz.openbmc_project.PLDM.FWU.CapabilitiesDuringUpdate` which exposes ++the capabilities of the component during update. ++4. `xyz.openbmc_project.PLDM.FWU.ComponentActivationMethods` which ++exposes the component activation methods. +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0030-Add-PLDM-version-purpose-enumeration.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0030-Add-PLDM-version-purpose-enumeration.patch new file mode 100644 index 000000000..0a57733aa --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0030-Add-PLDM-version-purpose-enumeration.patch @@ -0,0 +1,28 @@ +From 114687e8f5b4728bd5d82b3c2dbc97cb40273cb8 Mon Sep 17 00:00:00 2001 +From: Ayushi Smriti <smriti.ayushi@intel.com> +Date: Wed, 23 Sep 2020 22:01:25 +0530 +Subject: [PATCH] Add PLDM version purpose enumeration + +This change is to add PLDM in enumeration of possible purposes +of the version to support pldm type version purpose + +Change-Id: I7b914d4323bfe44a4e3cd60ed4a627aeceb6b56f +Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com> +--- + yaml/xyz/openbmc_project/Software/Version.interface.yaml | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/yaml/xyz/openbmc_project/Software/Version.interface.yaml b/yaml/xyz/openbmc_project/Software/Version.interface.yaml +index 345e5b51f653..f2efbecce938 100644 +--- a/yaml/xyz/openbmc_project/Software/Version.interface.yaml ++++ b/yaml/xyz/openbmc_project/Software/Version.interface.yaml +@@ -38,3 +38,6 @@ enumerations: + - name: PSU + description: > + The version is a version for a PSU. ++ - name: PLDM ++ description: > ++ The version is a version for PLDM. +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0031-update-meson-build-files-for-control-and-bios.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0031-update-meson-build-files-for-control-and-bios.patch new file mode 100644 index 000000000..31c5ad95b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0031-update-meson-build-files-for-control-and-bios.patch @@ -0,0 +1,149 @@ +From 20357179065a9157ad89f7c717ba214bf4fe4ded Mon Sep 17 00:00:00 2001 +From: Zhikui Ren <zhikui.ren@intel.com> +Date: Tue, 8 Dec 2020 15:08:21 -0800 +Subject: [PATCH] update meson build files for control and bios + +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +--- + .../Control/Power/RestoreDelay/meson.build | 13 +++++++++++++ + gen/xyz/openbmc_project/Control/Power/meson.build | 14 ++++++++++++++ + .../Control/Processor/ErrConfig/meson.build | 13 +++++++++++++ + .../openbmc_project/Control/Processor/meson.build | 13 +++++++++++++ + .../Inventory/Item/Bios/meson.build | 13 +++++++++++++ + gen/xyz/openbmc_project/Inventory/Item/meson.build | 14 ++++++++++++++ + 6 files changed, 80 insertions(+) + create mode 100644 gen/xyz/openbmc_project/Control/Power/RestoreDelay/meson.build + create mode 100644 gen/xyz/openbmc_project/Control/Processor/ErrConfig/meson.build + create mode 100644 gen/xyz/openbmc_project/Inventory/Item/Bios/meson.build + +diff --git a/gen/xyz/openbmc_project/Control/Power/RestoreDelay/meson.build b/gen/xyz/openbmc_project/Control/Power/RestoreDelay/meson.build +new file mode 100644 +index 000000000000..1afe8d26b87d +--- /dev/null ++++ b/gen/xyz/openbmc_project/Control/Power/RestoreDelay/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/Control/Power/RestoreDelay__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/Control/Power/RestoreDelay', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/Control/Power/meson.build b/gen/xyz/openbmc_project/Control/Power/meson.build +index dbd3b409ead0..43ccf1958aa9 100644 +--- a/gen/xyz/openbmc_project/Control/Power/meson.build ++++ b/gen/xyz/openbmc_project/Control/Power/meson.build +@@ -41,6 +41,20 @@ generated_others += custom_target( + ], + ) + ++subdir('RestoreDelay') ++generated_others += custom_target( ++ 'xyz/openbmc_project/Control/Power/RestoreDelay__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml', ], ++ output: [ 'RestoreDelay.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/Control/Power/RestoreDelay', ++ ], ++) ++ + subdir('RestorePolicy') + generated_others += custom_target( + 'xyz/openbmc_project/Control/Power/RestorePolicy__markdown'.underscorify(), +diff --git a/gen/xyz/openbmc_project/Control/Processor/ErrConfig/meson.build b/gen/xyz/openbmc_project/Control/Processor/ErrConfig/meson.build +new file mode 100644 +index 000000000000..937004d3d68b +--- /dev/null ++++ b/gen/xyz/openbmc_project/Control/Processor/ErrConfig/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/Control/Processor/ErrConfig__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/Control/Processor/ErrConfig', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/Control/Processor/meson.build b/gen/xyz/openbmc_project/Control/Processor/meson.build +index fc32bf2f877f..cc2a1d6507b3 100644 +--- a/gen/xyz/openbmc_project/Control/Processor/meson.build ++++ b/gen/xyz/openbmc_project/Control/Processor/meson.build +@@ -13,3 +13,16 @@ generated_others += custom_target( + ], + ) + ++subdir('ErrConfig') ++generated_others += custom_target( ++ 'xyz/openbmc_project/Control/Processor/ErrConfig__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml', ], ++ output: [ 'ErrConfig.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/Control/Processor/ErrConfig', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/Inventory/Item/Bios/meson.build b/gen/xyz/openbmc_project/Inventory/Item/Bios/meson.build +new file mode 100644 +index 000000000000..28fbf85dd97c +--- /dev/null ++++ b/gen/xyz/openbmc_project/Inventory/Item/Bios/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/Inventory/Item/Bios__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/Inventory/Item/Bios', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/Inventory/Item/meson.build b/gen/xyz/openbmc_project/Inventory/Item/meson.build +index 77d9be8b6c03..7d22037b32b8 100644 +--- a/gen/xyz/openbmc_project/Inventory/Item/meson.build ++++ b/gen/xyz/openbmc_project/Inventory/Item/meson.build +@@ -40,6 +40,20 @@ generated_others += custom_target( + ], + ) + ++subdir('Bios') ++generated_others += custom_target( ++ 'xyz/openbmc_project/Inventory/Item/Bios__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml', ], ++ output: [ 'Bios.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/Inventory/Item/Bios', ++ ], ++) ++ + subdir('Bmc') + generated_others += custom_target( + 'xyz/openbmc_project/Inventory/Item/Bmc__markdown'.underscorify(), +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0032-update-meson-build-for-MCTP-interfaces.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0032-update-meson-build-for-MCTP-interfaces.patch new file mode 100644 index 000000000..ab13f55fe --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0032-update-meson-build-for-MCTP-interfaces.patch @@ -0,0 +1,209 @@ +From f4eddacc871c6bb759e6e355a89ccaaa12c63396 Mon Sep 17 00:00:00 2001 +From: Zhikui Ren <zhikui.ren@intel.com> +Date: Tue, 8 Dec 2020 15:16:25 -0800 +Subject: [PATCH] update meson build for MCTP interfaces + +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +--- + gen/xyz/openbmc_project/MCTP/Base/meson.build | 13 ++++++ + .../MCTP/Binding/PCIe/meson.build | 13 ++++++ + .../MCTP/Binding/SMBus/meson.build | 13 ++++++ + .../openbmc_project/MCTP/Binding/meson.build | 30 +++++++++++++ + .../openbmc_project/MCTP/BusOwner/meson.build | 13 ++++++ + .../MCTP/SupportedMessageTypes/meson.build | 13 ++++++ + gen/xyz/openbmc_project/MCTP/meson.build | 42 +++++++++++++++++++ + 7 files changed, 137 insertions(+) + create mode 100644 gen/xyz/openbmc_project/MCTP/Base/meson.build + create mode 100644 gen/xyz/openbmc_project/MCTP/Binding/PCIe/meson.build + create mode 100644 gen/xyz/openbmc_project/MCTP/Binding/SMBus/meson.build + create mode 100644 gen/xyz/openbmc_project/MCTP/Binding/meson.build + create mode 100644 gen/xyz/openbmc_project/MCTP/BusOwner/meson.build + create mode 100644 gen/xyz/openbmc_project/MCTP/SupportedMessageTypes/meson.build + +diff --git a/gen/xyz/openbmc_project/MCTP/Base/meson.build b/gen/xyz/openbmc_project/MCTP/Base/meson.build +new file mode 100644 +index 000000000000..c605b2d496ac +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/Base/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/Base__cpp'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/MCTP/Base.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/MCTP/Base', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/MCTP/Binding/PCIe/meson.build b/gen/xyz/openbmc_project/MCTP/Binding/PCIe/meson.build +new file mode 100644 +index 000000000000..4573a64f4099 +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/Binding/PCIe/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/Binding/PCIe__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/MCTP/Binding/PCIe.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/MCTP/Binding/PCIe', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/MCTP/Binding/SMBus/meson.build b/gen/xyz/openbmc_project/MCTP/Binding/SMBus/meson.build +new file mode 100644 +index 000000000000..bfb4d040a54c +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/Binding/SMBus/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/Binding/SMBus__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/MCTP/Binding/SMBus.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/MCTP/Binding/SMBus', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/MCTP/Binding/meson.build b/gen/xyz/openbmc_project/MCTP/Binding/meson.build +new file mode 100644 +index 000000000000..36a74ddac294 +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/Binding/meson.build +@@ -0,0 +1,28 @@ ++# Generated file; do not modify. ++subdir('PCIe') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/Binding/PCIe__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/MCTP/Binding/PCIe.interface.yaml', ], ++ output: [ 'PCIe.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/MCTP/Binding/PCIe', ++ ], ++) ++ ++subdir('SMBus') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/Binding/SMBus__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/MCTP/Binding/SMBus.interface.yaml', ], ++ output: [ 'SMBus.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/MCTP/Binding/SMBus', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/MCTP/BusOwner/meson.build b/gen/xyz/openbmc_project/MCTP/BusOwner/meson.build +new file mode 100644 +index 000000000000..4b28bd6b34e7 +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/BusOwner/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/BusOwner__cpp'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/MCTP/BusOwner.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/MCTP/BusOwner', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/MCTP/SupportedMessageTypes/meson.build b/gen/xyz/openbmc_project/MCTP/SupportedMessageTypes/meson.build +new file mode 100644 +index 000000000000..4fd46823ed17 +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/SupportedMessageTypes/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/SupportedMessageTypes__cpp'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/MCTP/SupportedMessageTypes.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/MCTP/SupportedMessageTypes', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/MCTP/meson.build b/gen/xyz/openbmc_project/MCTP/meson.build +index b9598adf6fa7..6b6d00b833d2 100644 +--- a/gen/xyz/openbmc_project/MCTP/meson.build ++++ b/gen/xyz/openbmc_project/MCTP/meson.build +@@ -1,4 +1,33 @@ + # Generated file; do not modify. ++subdir('Base') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/Base__markdown'.underscorify(), ++ input: [ '../../../../yaml/xyz/openbmc_project/MCTP/Base.interface.yaml', ], ++ output: [ 'Base.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../yaml', ++ 'xyz/openbmc_project/MCTP/Base', ++ ], ++) ++ ++subdir('Binding') ++subdir('BusOwner') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/BusOwner__markdown'.underscorify(), ++ input: [ '../../../../yaml/xyz/openbmc_project/MCTP/BusOwner.interface.yaml', ], ++ output: [ 'BusOwner.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../yaml', ++ 'xyz/openbmc_project/MCTP/BusOwner', ++ ], ++) ++ + subdir('Endpoint') + generated_others += custom_target( + 'xyz/openbmc_project/MCTP/Endpoint__markdown'.underscorify(), +@@ -13,3 +42,16 @@ generated_others += custom_target( + ], + ) + ++subdir('SupportedMessageTypes') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/SupportedMessageTypes__markdown'.underscorify(), ++ input: [ '../../../../yaml/xyz/openbmc_project/MCTP/SupportedMessageTypes.interface.yaml', ], ++ output: [ 'SupportedMessageTypes.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../yaml', ++ 'xyz/openbmc_project/MCTP/SupportedMessageTypes', ++ ], ++) +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0033-update-meson-build-for-PLDM-FWU-interfaces.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0033-update-meson-build-for-PLDM-FWU-interfaces.patch new file mode 100644 index 000000000..6497d50d8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0033-update-meson-build-for-PLDM-FWU-interfaces.patch @@ -0,0 +1,418 @@ +From f73c64bbe2cf9369892269a71893e2911753ba4d Mon Sep 17 00:00:00 2001 +From: Zhikui Ren <zhikui.ren@intel.com> +Date: Tue, 8 Dec 2020 15:28:42 -0800 +Subject: [PATCH] update meson build for PLDM FWU interfaces + +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +--- + .../PLDM/FWU/ACPIDescriptor/meson.build | 13 ++ + .../ActiveComponentImageSetInfo/meson.build | 13 ++ + .../PLDM/FWU/ActiveComponentInfo/meson.build | 13 ++ + .../FWU/CapabilitiesDuringUpdate/meson.build | 13 ++ + .../ComponentActivationMethods/meson.build | 13 ++ + .../PLDM/FWU/FWUBase/meson.build | 13 ++ + .../PLDM/FWU/IANADescriptor/meson.build | 13 ++ + .../PLDM/FWU/PCIDescriptor/meson.build | 13 ++ + .../PendingComponentImageSetInfo/meson.build | 13 ++ + .../PLDM/FWU/PendingComponentInfo/meson.build | 13 ++ + .../PLDM/FWU/PnPDescriptor/meson.build | 13 ++ + gen/xyz/openbmc_project/PLDM/FWU/meson.build | 154 ++++++++++++++++++ + gen/xyz/openbmc_project/PLDM/meson.build | 1 + + 13 files changed, 298 insertions(+) + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/FWUBase/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/IANADescriptor/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/PCIDescriptor/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/PnPDescriptor/meson.build + create mode 100644 gen/xyz/openbmc_project/PLDM/FWU/meson.build + +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor/meson.build +new file mode 100644 +index 000000000000..fbaf6c458645 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ACPIDescriptor__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/ACPIDescriptor', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo/meson.build +new file mode 100644 +index 000000000000..af3df1483126 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo/meson.build +new file mode 100644 +index 000000000000..e5ff324231f5 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate/meson.build +new file mode 100644 +index 000000000000..03768e24090f +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods/meson.build +new file mode 100644 +index 000000000000..31cffd2e311b +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/FWUBase/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/FWUBase/meson.build +new file mode 100644 +index 000000000000..3b90b51e1b7f +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/FWUBase/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/FWUBase__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/FWUBase.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/FWUBase', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/IANADescriptor/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/IANADescriptor/meson.build +new file mode 100644 +index 000000000000..5d28fb5cca5a +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/IANADescriptor/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/IANADescriptor__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/IANADescriptor.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/IANADescriptor', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/PCIDescriptor/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/PCIDescriptor/meson.build +new file mode 100644 +index 000000000000..0c2a5d572dd1 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/PCIDescriptor/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PCIDescriptor__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/PCIDescriptor.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/PCIDescriptor', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo/meson.build +new file mode 100644 +index 000000000000..3fe7d85bbb29 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo/meson.build +new file mode 100644 +index 000000000000..a4d11a3317a4 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentInfo__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentInfo', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/PnPDescriptor/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/PnPDescriptor/meson.build +new file mode 100644 +index 000000000000..d0508ca366ae +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/PnPDescriptor/meson.build +@@ -0,0 +1,13 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PnPDescriptor__cpp'.underscorify(), ++ input: [ '../../../../../../yaml/xyz/openbmc_project/PLDM/FWU/PnPDescriptor.interface.yaml', ], ++ output: [ 'server.cpp', 'server.hpp', 'client.hpp', ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'cpp', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/PnPDescriptor', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/FWU/meson.build b/gen/xyz/openbmc_project/PLDM/FWU/meson.build +new file mode 100644 +index 000000000000..2bb71914b606 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/meson.build +@@ -0,0 +1,154 @@ ++# Generated file; do not modify. ++subdir('ACPIDescriptor') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ACPIDescriptor__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor.interface.yaml', ], ++ output: [ 'ACPIDescriptor.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/ACPIDescriptor', ++ ], ++) ++ ++subdir('ActiveComponentImageSetInfo') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo.interface.yaml', ], ++ output: [ 'ActiveComponentImageSetInfo.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo', ++ ], ++) ++ ++subdir('ActiveComponentInfo') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo.interface.yaml', ], ++ output: [ 'ActiveComponentInfo.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo', ++ ], ++) ++ ++subdir('CapabilitiesDuringUpdate') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate.interface.yaml', ], ++ output: [ 'CapabilitiesDuringUpdate.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate', ++ ], ++) ++ ++subdir('ComponentActivationMethods') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods.interface.yaml', ], ++ output: [ 'ComponentActivationMethods.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods', ++ ], ++) ++ ++subdir('FWUBase') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/FWUBase__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/FWUBase.interface.yaml', ], ++ output: [ 'FWUBase.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/FWUBase', ++ ], ++) ++ ++subdir('IANADescriptor') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/IANADescriptor__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/IANADescriptor.interface.yaml', ], ++ output: [ 'IANADescriptor.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/IANADescriptor', ++ ], ++) ++ ++subdir('PCIDescriptor') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PCIDescriptor__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/PCIDescriptor.interface.yaml', ], ++ output: [ 'PCIDescriptor.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/PCIDescriptor', ++ ], ++) ++ ++subdir('PendingComponentImageSetInfo') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo.interface.yaml', ], ++ output: [ 'PendingComponentImageSetInfo.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo', ++ ], ++) ++ ++subdir('PendingComponentInfo') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentInfo__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo.interface.yaml', ], ++ output: [ 'PendingComponentInfo.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentInfo', ++ ], ++) ++ ++subdir('PnPDescriptor') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PnPDescriptor__markdown'.underscorify(), ++ input: [ '../../../../../yaml/xyz/openbmc_project/PLDM/FWU/PnPDescriptor.interface.yaml', ], ++ output: [ 'PnPDescriptor.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.current_source_dir() / '../../../../../yaml', ++ 'xyz/openbmc_project/PLDM/FWU/PnPDescriptor', ++ ], ++) +diff --git a/gen/xyz/openbmc_project/PLDM/meson.build b/gen/xyz/openbmc_project/PLDM/meson.build +index 2be636cbaac0..35ff301901b3 100644 +--- a/gen/xyz/openbmc_project/PLDM/meson.build ++++ b/gen/xyz/openbmc_project/PLDM/meson.build +@@ -13,6 +13,7 @@ generated_others += custom_target( + ], + ) + ++subdir('FWU') + subdir('PDR') + generated_others += custom_target( + 'xyz/openbmc_project/PLDM/PDR__markdown'.underscorify(), +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0034-Add-username-property-to-SessionInfo-interface.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0034-Add-username-property-to-SessionInfo-interface.patch new file mode 100644 index 000000000..f614f88c6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0034-Add-username-property-to-SessionInfo-interface.patch @@ -0,0 +1,64 @@ +From ee72d92baf3d64d066e0ec64cbd8723177219182 Mon Sep 17 00:00:00 2001 +From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +Date: Tue, 1 Jun 2021 12:27:50 +0000 +Subject: [PATCH] Add username property to SessionInfo interface + +Add username property to SessionInfo interface to get username info on +individual IPMI session Id's on Redfish + +Tested: +Verified on SessionInfo D-bus interface. + +busctl introspect xyz.openbmc_project.Ipmi.Channel.eth0 + /xyz/openbmc_project/ipmi/session/eth0/0 +NAME TYPE SIGNATURE RESULT/VALUE FLAGS +...... +xyz.openbmc_project.Ipmi.SessionInfo interface - - - +.ChannelNum property y 0 emits-change writable +.CurrentPrivilege property y 0 emits-change writable +.RemoteIPAddr property u 2225389066 emits-change writable +.RemoteMACAddress property ay 0 emits-change writable +.RemotePort property q 35749 emits-change writable +.SessionHandle property y 0 emits-change writable +.State property y 0 emits-change writable +.UserID property y 0 emits-change writable +.Username property s "" emits-change writable + +Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +--- + yaml/xyz/openbmc_project/Ipmi/SESSION_README.md | 1 + + yaml/xyz/openbmc_project/Ipmi/SessionInfo.interface.yaml | 6 ++++++ + 2 files changed, 7 insertions(+) + +diff --git a/yaml/xyz/openbmc_project/Ipmi/SESSION_README.md b/yaml/xyz/openbmc_project/Ipmi/SESSION_README.md +index c59b251bb88d..ed41c3bf181d 100644 +--- a/yaml/xyz/openbmc_project/Ipmi/SESSION_README.md ++++ b/yaml/xyz/openbmc_project/Ipmi/SESSION_README.md +@@ -16,6 +16,7 @@ so that both phosphor-ipmi-host & phosphr-ipmi-net will be in sync. + * RemotePort - Remote port address. + * RemoteMACAddress -Remote MAC Address. + * UserID - Session created by given user id. ++* Username - Session created by given username. + + + +diff --git a/yaml/xyz/openbmc_project/Ipmi/SessionInfo.interface.yaml b/yaml/xyz/openbmc_project/Ipmi/SessionInfo.interface.yaml +index 0ddc0250db50..50aff5c26072 100644 +--- a/yaml/xyz/openbmc_project/Ipmi/SessionInfo.interface.yaml ++++ b/yaml/xyz/openbmc_project/Ipmi/SessionInfo.interface.yaml +@@ -46,6 +46,12 @@ properties: + Session created by given user ID. + errors: + - xyz.openbmc_project.Common.Error.InternalFailure ++ - name: Username ++ type: string ++ description: > ++ Session created by given username. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure + - name: State + type: byte + default: 0 +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend new file mode 100644 index 000000000..b8e0e7741 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend @@ -0,0 +1,22 @@ +# Keep this as a comment to enable the auto-bump script without +# stomping on SRC_URI from previous .bbappend files +#SRC_URI = "git://github.com/openbmc/phosphor-dbus-interfaces.git" +SRCREV = "e0764cf41d16b823a519e9d4f508b588e3e84aba" + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://0007-ipmi-set-BIOS-id.patch \ + file://0010-Increase-the-default-watchdog-timeout-value.patch \ + file://0012-Add-RestoreDelay-interface-for-power-restore-delay.patch \ + file://0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch \ + file://0024-Add-the-pre-timeout-interrupt-defined-in-IPMI-spec.patch \ + file://0025-Add-PreInterruptFlag-properity-in-DBUS.patch \ + file://0026-Add-StandbySpare-support-for-software-inventory.patch \ + file://0028-MCTP-Daemon-D-Bus-interface-definition.patch \ + file://0029-Add-D-Bus-interfaces-for-PLDM-FW-update.patch \ + file://0031-update-meson-build-files-for-control-and-bios.patch \ + file://0030-Add-PLDM-version-purpose-enumeration.patch \ + file://0032-update-meson-build-for-MCTP-interfaces.patch \ + file://0033-update-meson-build-for-PLDM-FWU-interfaces.patch \ + file://0034-Add-username-property-to-SessionInfo-interface.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper/xyz.openbmc_project.ObjectMapper.service b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper/xyz.openbmc_project.ObjectMapper.service new file mode 100644 index 000000000..9af9af254 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper/xyz.openbmc_project.ObjectMapper.service @@ -0,0 +1,20 @@ +[Unit] +Description=Phosphor DBus Service Discovery Manager +Before=obmc-mapper.target +After=dbus.socket + +[Service] +Restart=always +Type=dbus +ExecStart=/usr/bin/env mapperx \ + --service-namespaces="xyz. com. org." \ + --interface-namespaces="org. com. xyz." \ + --service-blacklists="org.freedesktop.systemd1" +SyslogIdentifier=phosphor-mapper +BusName={BUSNAME} +TimeoutStartSec=300 +RestartSec=5 +EnvironmentFile={envfiledir}/obmc/mapper + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper_%.bbappend new file mode 100644 index 000000000..4fc41d058 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper_%.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/0001-allow-dbus-sensors-without-thresholds.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/0001-allow-dbus-sensors-without-thresholds.patch new file mode 100644 index 000000000..6af0a5702 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/0001-allow-dbus-sensors-without-thresholds.patch @@ -0,0 +1,39 @@ +From 6b4071c12bdcc3bd20e72a7bd2eed19ff14b97af Mon Sep 17 00:00:00 2001 +From: Zhikui Ren <zhikui.ren@intel.com> +Date: Wed, 12 May 2021 19:11:08 -0700 +Subject: [PATCH] allow dbus sensors without thresholds + +catch dbus exceptions when query for thresholds interfaces. +log messages and continue. + +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +--- + dbus/dbusconfiguration.cpp | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/dbus/dbusconfiguration.cpp b/dbus/dbusconfiguration.cpp +index 55151d3..fd7a138 100644 +--- a/dbus/dbusconfiguration.cpp ++++ b/dbus/dbusconfiguration.cpp +@@ -286,13 +286,15 @@ void populatePidInfo( + { + interface = thresholds::criticalInterface; + } +- const std::string& path = sensorConfig.at(info.inputs.front()).readPath; + +- DbusHelper helper(sdbusplus::bus::new_system()); +- std::string service = helper.getService(interface, path); + double reading = 0; + try + { ++ const std::string& path = sensorConfig.at(info.inputs.front()).readPath; ++ ++ DbusHelper helper(sdbusplus::bus::new_system()); ++ std::string service = helper.getService(interface, path); ++ + helper.getProperty(service, path, interface, *thresholdProperty, + reading); + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service new file mode 100644 index 000000000..0e80b554a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service @@ -0,0 +1,14 @@ +[Unit] +Description=Phosphor-Pid-Control Margin-based Fan Control Daemon +After=xyz.openbmc_project.EntityManager.service +After=xyz.openbmc_project.ObjectMapper.service + +[Service] +Restart=always +ExecStart={bindir}/swampd +RestartSec=5 +StartLimitInterval=0 +Type=simple + +[Install] +WantedBy=basic.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend new file mode 100644 index 000000000..328176aca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend @@ -0,0 +1,14 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +inherit obmc-phosphor-systemd +SYSTEMD_SERVICE:${PN} = "phosphor-pid-control.service" +EXTRA_OECONF = "--enable-configure-dbus=yes" + +SRC_URI = "git://github.com/openbmc/phosphor-pid-control.git" +SRCREV = "cca9c659889d149c06e95bab4b8808db4f1e3eab" + +SRC_URI += "\ + file://0001-allow-dbus-sensors-without-thresholds.patch \ + " + +FILES:${PN} = "${bindir}/swampd ${bindir}/setsensor" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0002-Redfish-firmware-activation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0002-Redfish-firmware-activation.patch new file mode 100644 index 000000000..2a4c7e9b6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0002-Redfish-firmware-activation.patch @@ -0,0 +1,44 @@ +From b6b3051c8078267153712ed8cf514373924fd07a Mon Sep 17 00:00:00 2001 +From: Jennifer Lee <jennifer1.lee@intel.com> +Date: Mon, 16 Jul 2018 19:15:04 -0700 +Subject: [PATCH 2/6] Redfish firmware activation -- Modified flash.cpp to + call to customized flash service + +Signed-off-by: Jennifer Lee <jennifer1.lee@intel.com> +Change-Id: I81c3185e9c4c2ee907feeb53620faa22723c04d4 +--- + ubi/flash.cpp | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/ubi/flash.cpp b/ubi/flash.cpp +index ffa9348..5af2a17 100644 +--- a/ubi/flash.cpp ++++ b/ubi/flash.cpp +@@ -15,10 +15,13 @@ void Activation::flashWrite() + { + auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, + SYSTEMD_INTERFACE, "StartUnit"); +- method.append("obmc-flash-bmc-ubirw.service", "replace"); ++ std::string rwServiceFile = ++ "obmc-flash-bmc-ubirw@" + versionId + ".service"; ++ method.append(rwServiceFile, "replace"); + bus.call_noreply(method); + +- auto roServiceFile = "obmc-flash-bmc-ubiro@" + versionId + ".service"; ++ std::string roServiceFile = ++ "obmc-flash-bmc-ubiro@" + versionId + ".service"; + method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, + SYSTEMD_INTERFACE, "StartUnit"); + method.append(roServiceFile, "replace"); +@@ -37,7 +40,7 @@ void Activation::onStateChanges(sdbusplus::message::message& msg) + // Read the msg and populate each variable + msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); + +- auto rwServiceFile = "obmc-flash-bmc-ubirw.service"; ++ auto rwServiceFile = "obmc-flash-bmc-ubirw@" + versionId + ".service"; + auto roServiceFile = "obmc-flash-bmc-ubiro@" + versionId + ".service"; + auto ubootVarsServiceFile = + "obmc-flash-bmc-updateubootvars@" + versionId + ".service"; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0004-Changed-the-condition-of-software-version-service-wa.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0004-Changed-the-condition-of-software-version-service-wa.patch new file mode 100644 index 000000000..bcf692f5e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0004-Changed-the-condition-of-software-version-service-wa.patch @@ -0,0 +1,41 @@ +From 1b00440d0c8fabfa2e3eda984a21c0f004ca2150 Mon Sep 17 00:00:00 2001 +From: Jennifer Lee <jennifer1.lee@intel.com> +Date: Fri, 26 Oct 2018 11:54:05 -0700 +Subject: [PATCH 4/6] Changed the condition of software version service + watching deamon + + Originally it watches only files that are "written" into /tmp/images directory. +This change modified the condition to also watch files that are "moved" into this directory. + +Signed-off-by: Jennifer Lee <jennifer1.lee@intel.com> +Change-Id: I3e9cf1ffc3f5350d4649d32d3d3837991322a65b +--- + watch.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/watch.cpp b/watch.cpp +index e46b8aa..eee1bc3 100644 +--- a/watch.cpp ++++ b/watch.cpp +@@ -46,7 +46,7 @@ Watch::Watch(sd_event* loop, std::function<int(std::string&)> imageCallback) : + std::strerror(error)); + } + +- wd = inotify_add_watch(fd, IMG_UPLOAD_DIR, IN_CLOSE_WRITE); ++ wd = inotify_add_watch(fd, IMG_UPLOAD_DIR, IN_CLOSE_WRITE | IN_MOVED_TO); + if (-1 == wd) + { + auto error = errno; +@@ -97,7 +97,8 @@ int Watch::callback(sd_event_source* /* s */, int fd, uint32_t revents, + while (offset < bytes) + { + auto event = reinterpret_cast<inotify_event*>(&buffer[offset]); +- if ((event->mask & IN_CLOSE_WRITE) && !(event->mask & IN_ISDIR)) ++ if ((event->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) && ++ !(event->mask & IN_ISDIR)) + { + auto tarballPath = std::string{IMG_UPLOAD_DIR} + '/' + event->name; + auto rc = static_cast<Watch*>(userdata)->imageCallback(tarballPath); +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0005-Modified-firmware-activation-to-launch-fwupd.sh-thro.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0005-Modified-firmware-activation-to-launch-fwupd.sh-thro.patch new file mode 100644 index 000000000..fcdc75f8f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0005-Modified-firmware-activation-to-launch-fwupd.sh-thro.patch @@ -0,0 +1,225 @@ +From 6e95f4a761021e3204912e3a7c5b80a18d9f16cf Mon Sep 17 00:00:00 2001 +From: Jennifer Lee <jennifer1.lee@intel.com> +Date: Mon, 10 Dec 2018 10:36:44 -0800 +Subject: [PATCH] Modified firmware activation to launch fwupd.sh through + + non-ubi fs code path to match more closely to the upstream design - + Added option FWUPD_SCRIPT to saperate intel customized code - Adopted + ActivationProgress from ubi fs activation code mainly for progress indicator + for ipmi update + +Signed-off-by: Jennifer Lee <jennifer1.lee@intel.com> +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + activation.cpp | 50 +++++++++++++++++++++++++++++++++++++---------- + meson.build | 2 ++ + meson_options.txt | 3 +++ + static/flash.cpp | 42 +++++++++++++++++++++++++++++++++++++-- + ubi/flash.cpp | 9 +++------ + 5 files changed, 88 insertions(+), 18 deletions(-) + +diff --git a/activation.cpp b/activation.cpp +index 5490cd9974b3..e43959d78ed2 100644 +--- a/activation.cpp ++++ b/activation.cpp +@@ -88,20 +88,50 @@ auto Activation::activation(Activations value) -> Activations + + if (value == softwareServer::Activation::Activations::Activating) + { +-#ifdef WANT_SIGNATURE_VERIFY +- fs::path uploadDir(IMG_UPLOAD_DIR); +- if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH)) ++#ifdef FWUPD_SCRIPT ++ if (!activationProgress) + { +- onVerifyFailed(); +- // Stop the activation process, if fieldMode is enabled. +- if (parent.control::FieldMode::fieldModeEnabled()) ++ // Enable systemd signals ++ Activation::subscribeToSystemdSignals(); ++ parent.freeSpace(*this); ++ ++ activationProgress = ++ std::make_unique<ActivationProgress>(bus, path); ++ ++#ifdef WANT_SIGNATURE_VERIFY ++ fs::path uploadDir(IMG_UPLOAD_DIR); ++ if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH)) + { +- return softwareServer::Activation::activation( +- softwareServer::Activation::Activations::Failed); ++ onVerifyFailed(); ++ // Stop the activation process, if fieldMode is enabled. ++ if (parent.control::FieldMode::fieldModeEnabled()) ++ { ++ return softwareServer::Activation::activation( ++ softwareServer::Activation::Activations::Failed); ++ } + } +- } + #endif ++ flashWrite(); ++ } ++ else if (activationProgress->progress() == 100) ++ { ++ error("progress == 100..."); ++ if (!redundancyPriority) ++ { ++ redundancyPriority = ++ std::make_unique<RedundancyPriority>(bus, path, *this, 0); ++ } ++ ++ // Remove version object from image manager ++ Activation::deleteImageManagerObject(); + ++ // Create active association ++ parent.createActiveAssociation(path); ++ ++ return softwareServer::Activation::activation( ++ softwareServer::Activation::Activations::Active); ++ } ++#else // !FWUPD_SCRIPT + #ifdef HOST_BIOS_UPGRADE + auto purpose = parent.versions.find(versionId)->second->purpose(); + if (purpose == VersionPurpose::Host) +@@ -124,7 +154,6 @@ auto Activation::activation(Activations value) -> Activations + return softwareServer::Activation::activation(value); + } + #endif +- + auto versionStr = parent.versions.find(versionId)->second->version(); + + if (!minimum_ship_level::verify(versionStr)) +@@ -174,6 +203,7 @@ auto Activation::activation(Activations value) -> Activations + return softwareServer::Activation::activation( + softwareServer::Activation::Activations::Active); + #endif ++#endif // FWUPD_SCRIPT + } + else + { +diff --git a/meson.build b/meson.build +index a6ebcc43c0ec..5c7900924959 100644 +--- a/meson.build ++++ b/meson.build +@@ -59,6 +59,8 @@ conf.set('WANT_SIGNATURE_VERIFY', \ + get_option('verify-full-signature').enabled()) + conf.set('WANT_SIGNATURE_FULL_VERIFY', get_option('verify-full-signature').enabled()) + ++conf.set('FWUPD_SCRIPT', get_option('fwupd-script').enabled()) ++ + # Configurable variables + conf.set('ACTIVE_BMC_MAX_ALLOWED', get_option('active-bmc-max-allowed')) + conf.set_quoted('HASH_FILE_NAME', get_option('hash-file-name')) +diff --git a/meson_options.txt b/meson_options.txt +index 4def7f982809..74e757b75f01 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -25,6 +25,9 @@ option('verify-signature', type: 'feature', + option('verify-full-signature', type: 'feature', + description: 'Enable image full signature validation.') + ++option('fwupd-script', type: 'feature', ++ description: 'Enable fwupd script support.') ++ + # Variables + option( + 'active-bmc-max-allowed', type: 'integer', +diff --git a/static/flash.cpp b/static/flash.cpp +index 101828b1ade5..5506a59ac499 100644 +--- a/static/flash.cpp ++++ b/static/flash.cpp +@@ -22,9 +22,11 @@ namespace updater + + namespace fs = std::filesystem; + using namespace phosphor::software::image; ++namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server; + + void Activation::flashWrite() + { ++#ifndef FWUPD_SCRIPT + // For static layout code update, just put images in /run/initramfs. + // It expects user to trigger a reboot and an updater script will program + // the image to flash during reboot. +@@ -36,11 +38,47 @@ void Activation::flashWrite() + fs::copy_file(uploadDir / versionId / bmcImage, toPath / bmcImage, + fs::copy_options::overwrite_existing); + } ++ ++#else ++ auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, ++ SYSTEMD_INTERFACE, "StartUnit"); ++ method.append("fwupd@" + versionId + ".service", "replace"); ++ bus.call_noreply(method); ++#endif + } + +-void Activation::onStateChanges(sdbusplus::message::message& /*msg*/) ++void Activation::onStateChanges(__attribute__((unused)) ++ sdbusplus::message::message& msg) + { +- // Empty ++#ifndef FWUPD_SCRIPT ++ uint32_t newStateID{}; ++ sdbusplus::message::object_path newStateObjPath; ++ std::string newStateUnit{}; ++ std::string newStateResult{}; ++ ++ msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); ++ ++ auto rwServiceFile = "fwupdw@" + versionId + ".service"; ++ ++ if (newStateUnit == rwServiceFile && newStateResult == "done") ++ { ++ activationProgress->progress(100); ++ } ++ ++ if (newStateUnit == rwServiceFile) ++ { ++ if (newStateResult == "failed" || newStateResult == "dependency") ++ { ++ Activation::activation( ++ softwareServer::Activation::Activations::Failed); ++ } ++ else ++ { ++ Activation::activation( ++ softwareServer::Activation::Activations::Activating); ++ } ++ } ++#endif + } + + } // namespace updater +diff --git a/ubi/flash.cpp b/ubi/flash.cpp +index a263bfb81116..c58eefc4ec48 100644 +--- a/ubi/flash.cpp ++++ b/ubi/flash.cpp +@@ -15,13 +15,10 @@ void Activation::flashWrite() + { + auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, + SYSTEMD_INTERFACE, "StartUnit"); +- std::string rwServiceFile = +- "obmc-flash-bmc-ubirw@" + versionId + ".service"; +- method.append(rwServiceFile, "replace"); ++ method.append("obmc-flash-bmc-ubirw.service", "replace"); + bus.call_noreply(method); + +- std::string roServiceFile = +- "obmc-flash-bmc-ubiro@" + versionId + ".service"; ++ auto roServiceFile = "obmc-flash-bmc-ubiro@" + versionId + ".service"; + method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, + SYSTEMD_INTERFACE, "StartUnit"); + method.append(roServiceFile, "replace"); +@@ -40,7 +37,7 @@ void Activation::onStateChanges(sdbusplus::message::message& msg) + // Read the msg and populate each variable + msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); + +- auto rwServiceFile = "obmc-flash-bmc-ubirw@" + versionId + ".service"; ++ auto rwServiceFile = "obmc-flash-bmc-ubirw.service"; + auto roServiceFile = "obmc-flash-bmc-ubiro@" + versionId + ".service"; + auto ubootVarsServiceFile = + "obmc-flash-bmc-updateubootvars@" + versionId + ".service"; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0006-Modify-the-ID-of-software-image-updater-object-on-DB.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0006-Modify-the-ID-of-software-image-updater-object-on-DB.patch new file mode 100644 index 000000000..1f2a86fb9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0006-Modify-the-ID-of-software-image-updater-object-on-DB.patch @@ -0,0 +1,44 @@ +From 9b3c44e9fb3d907c0152f14b967e23ab964c0e0b Mon Sep 17 00:00:00 2001 +From: Jennifer Lee <jennifer1.lee@intel.com> +Date: Thu, 14 Feb 2019 14:54:45 -0800 +Subject: [PATCH 6/6] Modify the ID of software image updater object on DBus to + allow force update onto same version image + +In the original design of image update, it does not allow the same version of image to be flashed onto itself. +But this blocks validation tests and in most of the cases we don't prevent user from doing such update. + +This patch appends a random number after the version ID hash string to unblock such limitation. + +Signed-off-by: Jennifer Lee <jennifer1.lee@intel.com> +Change-Id: I16aba4804ae1bc2e8784320f91c0419fb8b23c35 +--- + image_manager.cpp | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/image_manager.cpp b/image_manager.cpp +index 5b2ff49..e3d26e3 100644 +--- a/image_manager.cpp ++++ b/image_manager.cpp +@@ -9,6 +9,7 @@ + #include <stdlib.h> + #include <sys/stat.h> + #include <sys/wait.h> ++#include <time.h> + #include <unistd.h> + + #include <elog-errors.hpp> +@@ -174,6 +175,11 @@ int Manager::processImage(const std::string& tarFilePath) + // Compute id + auto id = Version::getId(version); + ++ // Append a random number after the original version hash ++ // This will allow forcing image update onto the same version ++ srand(time(NULL)); ++ id = id + "_" + std::to_string(rand()); ++ + fs::path imageDirPath = std::string{IMG_UPLOAD_DIR}; + imageDirPath /= id; + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0007-Adding-StandBySpare-for-firmware-activation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0007-Adding-StandBySpare-for-firmware-activation.patch new file mode 100644 index 000000000..6039be44b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0007-Adding-StandBySpare-for-firmware-activation.patch @@ -0,0 +1,76 @@ +From c2ae3ac444f7a5e9674a82f47086874f947bcec6 Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@linux.intel.com> +Date: Thu, 5 Dec 2019 12:38:21 +0530 +Subject: [PATCH] Adding StandBySpare for firmware activation + +Added new states 'StandBySpare', 'ActivatingAsStandbySpare' for +firmware activation. If the uploaded firmware image is for +backup/recovery, then it sets the "StandBySpare" value for +Activations. When backup/recovery image is in activating state, +then activations will be set to "ActivatingAsStandbySpare". + +Tested: +Tested using redfish interface. +Did the GET on "/redfish/v1/UpdateService/FirmwareInventory/<backup image>" +Response: + .... + "Status": { + "Health": "OK", + "HealthRollup": "OK", + "State": "StandbySpare" + } +....... + +Change-Id: I7f1608fac3196774a6d593b6128d58da3f5c88fc +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@linux.intel.com> +--- + activation.cpp | 22 ++++++++++++++++++++-- + 1 file changed, 20 insertions(+), 2 deletions(-) + +diff --git a/activation.cpp b/activation.cpp +index 2966b2f..a098784 100644 +--- a/activation.cpp ++++ b/activation.cpp +@@ -81,12 +81,16 @@ void Activation::unsubscribeFromSystemdSignals() + auto Activation::activation(Activations value) -> Activations + { + if ((value != softwareServer::Activation::Activations::Active) && +- (value != softwareServer::Activation::Activations::Activating)) ++ (value != softwareServer::Activation::Activations::Activating) && ++ (value != ++ softwareServer::Activation::Activations::ActivatingAsStandbySpare)) + { + redundancyPriority.reset(nullptr); + } + +- if (value == softwareServer::Activation::Activations::Activating) ++ if (value == softwareServer::Activation::Activations::Activating || ++ value == ++ softwareServer::Activation::Activations::ActivatingAsStandbySpare) + { + #ifdef FWUPD_SCRIPT + if (!activationProgress) +@@ -309,6 +313,20 @@ auto Activation::requestedActivation(RequestedActivations value) + softwareServer::Activation::Activations::Activating); + } + } ++ else if ((value == ++ softwareServer::Activation::RequestedActivations::StandbySpare) && ++ (softwareServer::Activation::requestedActivation() != ++ softwareServer::Activation::RequestedActivations::StandbySpare)) ++ { ++ if ((softwareServer::Activation::activation() == ++ softwareServer::Activation::Activations::Ready) || ++ (softwareServer::Activation::activation() == ++ softwareServer::Activation::Activations::Failed)) ++ { ++ Activation::activation(softwareServer::Activation::Activations:: ++ ActivatingAsStandbySpare); ++ } ++ } + return softwareServer::Activation::requestedActivation(value); + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0007-PFR-images-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0007-PFR-images-support.patch new file mode 100644 index 000000000..e1a7f1746 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0007-PFR-images-support.patch @@ -0,0 +1,411 @@ +From 51fd06346ff492d5d4862886cb34e024d05edb45 Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@intel.com> +Date: Tue, 13 Aug 2019 22:43:12 +0530 +Subject: [PATCH] PFR images support in phosphor-software-manager + +This commit adds support for handling the PFR images +upload and processing. + +Testing: +tested PFR image uploads and updates + +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> + +--- + item_updater.cpp | 7 +- + meson.build | 8 +- + meson_options.txt | 3 + + pfr_image_manager.cpp | 218 ++++++++++++++++++++++++++++++++++++++++++ + pfr_image_manager.hpp | 76 +++++++++++++++ + 5 files changed, 308 insertions(+), 4 deletions(-) + create mode 100644 pfr_image_manager.cpp + create mode 100644 pfr_image_manager.hpp + +diff --git a/item_updater.cpp b/item_updater.cpp +index 81fc01f..60e7d23 100644 +--- a/item_updater.cpp ++++ b/item_updater.cpp +@@ -65,10 +65,10 @@ void ItemUpdater::createActivation(sdbusplus::message::message& msg) + auto value = SVersion::convertVersionPurposeFromString( + std::get<std::string>(property.second)); + if (value == VersionPurpose::BMC || +-#ifdef HOST_BIOS_UPGRADE ++#if defined(HOST_BIOS_UPGRADE) || defined(PFR_UPDATE) + value == VersionPurpose::Host || + #endif +- value == VersionPurpose::System) ++ value == VersionPurpose::Other) + { + purpose = value; + } +@@ -414,6 +414,7 @@ void ItemUpdater::deleteAll() + ItemUpdater::ActivationStatus + ItemUpdater::validateSquashFSImage(const std::string& filePath) + { ++#ifndef PFR_UPDATE + bool valid = true; + + // Record the images which are being updated +@@ -431,7 +432,7 @@ ItemUpdater::ActivationStatus + return ItemUpdater::ActivationStatus::invalid; + } + } +- ++#endif + return ItemUpdater::ActivationStatus::ready; + } + +diff --git a/meson.build b/meson.build +index 5c79009..e33998d 100644 +--- a/meson.build ++++ b/meson.build +@@ -60,6 +60,7 @@ conf.set('WANT_SIGNATURE_VERIFY', \ + conf.set('WANT_SIGNATURE_FULL_VERIFY', get_option('verify-full-signature').enabled()) + + conf.set('FWUPD_SCRIPT', get_option('fwupd-script').enabled()) ++conf.set('PFR_UPDATE', get_option('pfr-update').enabled()) + + # Configurable variables + conf.set('ACTIVE_BMC_MAX_ALLOWED', get_option('active-bmc-max-allowed')) +@@ -205,12 +206,17 @@ executable( + install: true + ) + ++image_manager_source = files('image_manager.cpp') ++if get_option('pfr-update').enabled() ++ image_manager_source = files('pfr_image_manager.cpp') ++endif ++ + executable( + 'phosphor-version-software-manager', + image_error_cpp, + image_error_hpp, +- 'image_manager.cpp', + 'image_manager_main.cpp', ++ image_manager_source, + 'version.cpp', + 'watch.cpp', + dependencies: [deps, ssl], +diff --git a/meson_options.txt b/meson_options.txt +index da257b7..2204e04 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -28,6 +28,9 @@ option('verify-full-signature', type: 'feature', + option('fwupd-script', type: 'feature', + description: 'Enable fwupd script support.') + ++option('pfr-update', type: 'feature', ++ description: 'Enable PFR image support.') ++ + # Variables + option( + 'active-bmc-max-allowed', type: 'integer', +diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp +new file mode 100644 +index 0000000..fec1e94 +--- /dev/null ++++ b/pfr_image_manager.cpp +@@ -0,0 +1,218 @@ ++#include "config.h" ++ ++#include "pfr_image_manager.hpp" ++ ++#include "version.hpp" ++#include "watch.hpp" ++ ++#include <stdio.h> ++#include <stdlib.h> ++#include <sys/stat.h> ++#include <sys/wait.h> ++#include <time.h> ++#include <unistd.h> ++ ++#include <elog-errors.hpp> ++#include <xyz/openbmc_project/Software/Image/error.hpp> ++ ++#include <algorithm> ++#include <cstring> ++#include <filesystem> ++#include <fstream> ++#include <iomanip> ++#include <sstream> ++#include <string> ++ ++namespace phosphor ++{ ++namespace software ++{ ++namespace manager ++{ ++ ++using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error; ++namespace Software = phosphor::logging::xyz::openbmc_project::Software; ++ ++static constexpr const uint32_t pfmPos = 2054; ++ ++static int getPFRImgInfo(const std::filesystem::path imgPath, uint8_t& imgType, ++ std::string& version) ++{ ++ struct pfrImgBlock0 block0Data; ++ uint8_t verData[2]; ++ ++ if (std::filesystem::exists(imgPath)) ++ { ++ try ++ { ++ std::ifstream imgFile(imgPath, std::ios::binary | std::ios::in); ++ ++ if (!imgFile.good()) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Image file read failed"); ++ return -1; ++ } ++ ++ imgFile.read(reinterpret_cast<char*>(&block0Data), ++ sizeof(block0Data)); ++ imgType = block0Data.pcType[0]; ++ imgFile.seekg(pfmPos, ++ std::ios::beg); // Version is at 0x806 in the PFM ++ imgFile.read(reinterpret_cast<char*>(&verData), sizeof(verData)); ++ imgFile.close(); ++ version = ++ std::to_string(verData[0]) + "." + std::to_string(verData[1]); ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "PFR image", ++ phosphor::logging::entry("PCType=%d", block0Data.pcType[0]), ++ phosphor::logging::entry("VERSION=%s", version.c_str())); ++ } ++ catch (std::exception& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++int Manager::processImage(const std::string& imgFilePath) ++{ ++ std::filesystem::path imgPath(imgFilePath); ++ ++ if (!std::filesystem::exists(imgPath)) ++ return -1; ++ ++ uint8_t imgType; ++ int retry = 3; ++ std::string ver; ++ std::string purposeString; ++ ++ if (0 != getPFRImgInfo(imgFilePath, imgType, ver)) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error reading uploaded image type and version"); ++ return -1; ++ } ++ ++ if (ver.empty()) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Empty version from image file"); ++ return -1; ++ } ++ ++ if (imgType == pfrBMCUpdateCap) ++ { ++ purposeString = ++ "xyz.openbmc_project.Software.Version.VersionPurpose.BMC"; ++ } ++ else if (imgType == pfrPCHUpdateCap) ++ { ++ purposeString = ++ "xyz.openbmc_project.Software.Version.VersionPurpose.Host"; ++ } ++ else if (imgType == pfrCPLDUpdateCap) ++ { ++ purposeString = ++ "xyz.openbmc_project.Software.Version.VersionPurpose.Other"; ++ } ++ else ++ { ++ purposeString = ++ "xyz.openbmc_project.Software.Version.VersionPurpose.Unknown"; ++ ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unknown image type"); ++ return -1; ++ } ++ ++ sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose ++ purpose = Version::VersionPurpose::Unknown; ++ try ++ { ++ purpose = Version::convertVersionPurposeFromString(purposeString); ++ } ++ catch (const sdbusplus::exception::InvalidEnumString& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error: Failed to convert purpose to enum." ++ " Setting to Unknown."); ++ } ++ ++ // Compute id ++ std::string id = Version::getId(ver); ++ ++ // Append a random number after the original version hash ++ // This will allow forcing image update onto the same version ++ // with 3 retries on random number generation. ++ do ++ { ++ srand(time(NULL)); ++ id = id + "_" + std::to_string(rand()); ++ } while ((versions.find(id) != versions.end()) && retry--); ++ ++ if (versions.find(id) != versions.end()) ++ { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "Software Object with the same version already exists, exiting " ++ "the update", ++ phosphor::logging::entry("VERSION_ID=%s", id.c_str())); ++ ++ return -1; ++ } ++ ++ std::filesystem::path imageDirPath(IMG_UPLOAD_DIR); ++ imageDirPath /= id; ++ ++ std::filesystem::create_directory(imageDirPath); ++ ++ std::filesystem::path newFileName = imageDirPath / "image-runtime"; ++ std::filesystem::rename(imgFilePath, newFileName); ++ ++ // Create Version object ++ std::string objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; ++ ++ auto versionPtr = std::make_unique<Version>( ++ bus, objPath, ver, purpose, imageDirPath.string(), ++ std::bind(&Manager::erase, this, std::placeholders::_1)); ++ versionPtr->deleteObject = ++ std::make_unique<phosphor::software::manager::Delete>(bus, objPath, ++ *versionPtr); ++ versions.insert(std::make_pair(id, std::move(versionPtr))); ++ ++ return 0; ++} ++ ++void Manager::erase(std::string entryId) ++{ ++ auto it = versions.find(entryId); ++ if (it == versions.end()) ++ { ++ return; ++ } ++ ++ if (it->second->isFunctional()) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ ("Error: Version " + entryId + ++ " is currently running on the BMC." ++ " Unable to remove.") ++ .c_str()); ++ return; ++ } ++ ++ // Delete image dir ++ std::filesystem::path imageDirPath = (*(it->second)).path(); ++ if (std::filesystem::exists(imageDirPath)) ++ { ++ std::filesystem::remove_all(imageDirPath); ++ } ++ this->versions.erase(entryId); ++} ++ ++} // namespace manager ++} // namespace software ++} // namespace phosphor +diff --git a/pfr_image_manager.hpp b/pfr_image_manager.hpp +new file mode 100644 +index 0000000..b9a5822 +--- /dev/null ++++ b/pfr_image_manager.hpp +@@ -0,0 +1,76 @@ ++#pragma once ++#include "version.hpp" ++ ++#include <sdbusplus/server.hpp> ++ ++namespace phosphor ++{ ++namespace software ++{ ++namespace manager ++{ ++ ++enum pfrImgPCType ++{ ++ pfrCPLDUpdateCap = 0x00, ++ pfrPCHPFM = 0x01, ++ pfrPCHUpdateCap = 0x02, ++ pfrBMCPFM = 0x03, ++ pfrBMCUpdateCap = 0x04 ++}; ++ ++/* PFR image block 0 - As defined in HAS */ ++struct pfrImgBlock0 ++{ ++ uint8_t tag[4]; ++ uint8_t pcLength[4]; ++ uint8_t pcType[4]; ++ uint8_t reserved1[4]; ++ uint8_t hash256[32]; ++ uint8_t hash384[48]; ++ uint8_t reserved2[32]; ++} __attribute__((packed)); ++ ++/** @class Manager ++ * @brief Contains a map of Version dbus objects. ++ * @details The software image manager class that contains the Version dbus ++ * objects and their version ids. ++ */ ++class Manager ++{ ++ public: ++ /** @brief Constructs Manager Class ++ * ++ * @param[in] bus - The Dbus bus object ++ */ ++ Manager(sdbusplus::bus::bus& bus) : bus(bus){}; ++ ++ /** ++ * @brief Verify the image and provide the image to updater. ++ * Create and populate the version and file path interfaces. ++ * ++ * @param[in] uploaded image. ++ * @param[out] result - 0 if successful. ++ */ ++ int processImage(const std::string& imageFilePath); ++ ++ /** ++ * @brief Erase specified entry d-bus object ++ * and deletes the image file. ++ * ++ * @param[in] entryId - unique identifier of the entry ++ */ ++ void erase(std::string entryId); ++ ++ private: ++ /** @brief Persistent map of Version dbus objects and their ++ * version id */ ++ std::map<std::string, std::unique_ptr<Version>> versions; ++ ++ /** @brief Persistent sdbusplus DBus bus connection. */ ++ sdbusplus::bus::bus& bus; ++}; ++ ++} // namespace manager ++} // namespace software ++} // namespace phosphor +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0008-PFR-image-HASH-verification.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0008-PFR-image-HASH-verification.patch new file mode 100644 index 000000000..72eb0beba --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0008-PFR-image-HASH-verification.patch @@ -0,0 +1,415 @@ +From ac6e0c217a1b136d82f93b691aff1acb40009f26 Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@linux.intel.com> +Date: Thu, 5 Dec 2019 11:55:36 +0530 +Subject: [PATCH] PFR image HASH verification + +This adds HASH verification on PFR images uploaded for +firmware updates + +Tested: tested firmware update with good and bad HASH images. + +A) +1. Upload the corrupted image for fw update. +2. Image present in /tmp/images/ +-rw-r--r-- 1 root root 22969344 Jun 3 09:27 +5dea710b-8b85-4065-8af7-3149ada81edf + +3. Journalctl logs during image verification +Jun 03 09:27:20 intel-obmc phosphor-version-software-manager[4755]: +Firmware image HASH verification failed +Jun 03 09:27:20 intel-obmc phosphor-version-software-manager[4755]: +Error verifying uploaded image +Jun 03 09:27:20 intel-obmc phosphor-version-software-manager[4755]: +Error processing image + +4. image deleted from /tmp/images/ + +B) +1. Upload the correct image. +POST: https://<BMC_IP>/redfish/v1/UpdateService/ + with <BMC_signed_cap> binary file +2. Image verification is success and proceeds with update. +{ + "@odata.id": "/redfish/v1/TaskService/Tasks/0", + "@odata.type": "#Task.v1_4_3.Task", + "Id": "0", + "TaskState": "Running", + "TaskStatus": "OK" +} + +Change-Id: I9336980bfb74c8136690024782bfef45f6b08d56 +Signed-off-by: Chalapathi Venkataramashetty <chalapathix.venkataramashetty@intel.com> + +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@linux.intel.com> +--- + pfr_image_manager.cpp | 150 +++++++++++++++++++++++++++++++++---------- + pfr_image_manager.hpp | 112 +++++++++++++++++++++++++++++-- + 2 files changed, 222 insertions(+), 40 deletions(-) + +diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp +index 242a6ca..1a41cbe 100644 +--- a/pfr_image_manager.cpp ++++ b/pfr_image_manager.cpp +@@ -5,6 +5,8 @@ + #include "version.hpp" + #include "watch.hpp" + ++#include <fcntl.h> ++#include <openssl/err.h> + #include <stdio.h> + #include <stdlib.h> + #include <sys/stat.h> +@@ -20,6 +22,7 @@ + #include <filesystem> + #include <fstream> + #include <iomanip> ++#include <set> + #include <sstream> + #include <string> + +@@ -34,12 +37,21 @@ using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error; + namespace Software = phosphor::logging::xyz::openbmc_project::Software; + + static constexpr const uint32_t pfmPos = 2054; ++static constexpr const uint32_t block0Magic = 0xB6EAFD19; ++static constexpr const uint32_t lengthBlk0Blk1 = 1024; + +-static int getPFRImgInfo(const std::filesystem::path imgPath, uint8_t& imgType, +- std::string& version) ++int Manager::verifyPFRImage(const std::filesystem::path imgPath, ++ std::string& version, std::string& purposeString) + { +- struct pfrImgBlock0 block0Data; +- uint8_t verData[2]; ++ uint8_t imgType = 0; ++ uint32_t imgMagic = 0; ++ uint8_t verData[2] = {0}; ++ uint32_t hashLen = 0; ++ struct pfrImgBlock0 block0Data = {}; ++ ++ std::string imageName; ++ ++ EVP_MD_CTX* ctx; + + if (std::filesystem::exists(imgPath)) + { +@@ -56,17 +68,101 @@ static int getPFRImgInfo(const std::filesystem::path imgPath, uint8_t& imgType, + + imgFile.read(reinterpret_cast<char*>(&block0Data), + sizeof(block0Data)); ++ ++ imgMagic = block0Data.tag; ++ ++ if (imgMagic != block0Magic) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Image magic number match failed", ++ phosphor::logging::entry("IMAGEMAGIC=0x%x", imgMagic)); ++ return -1; ++ } ++ + imgType = block0Data.pcType[0]; ++ ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "Image Type", phosphor::logging::entry( ++ "IMAGETYPE=0x%x", static_cast<int>(imgType))); ++ ++ if (imgType == pfrBMCUpdateCap || imgType == pfrBMCPFM) ++ { ++ imageName = "BMC"; ++ purposeString = ++ "xyz.openbmc_project.Software.Version.VersionPurpose.BMC"; ++ } ++ else if (imgType == pfrPCHUpdateCap || imgType == pfrPCHPFM) ++ { ++ imageName = "BIOS"; ++ purposeString = ++ "xyz.openbmc_project.Software.Version.VersionPurpose.Host"; ++ } ++ else if (imgType == pfrCPLDUpdateCap) ++ { ++ imageName = "CPLD"; ++ purposeString = ++ "xyz.openbmc_project.Software.Version.VersionPurpose.Other"; ++ } ++ else ++ { ++ purposeString = "xyz.openbmc_project.Software.Version." ++ "VersionPurpose.Unknown"; ++ ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unknown image type"); ++ return -1; ++ } ++ + imgFile.seekg(pfmPos, + std::ios::beg); // Version is at 0x806 in the PFM + imgFile.read(reinterpret_cast<char*>(&verData), sizeof(verData)); + imgFile.close(); +- version = +- std::to_string(verData[0]) + "." + std::to_string(verData[1]); ++ ++ auto size = std::filesystem::file_size(imgPath); ++ ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "Image Size", phosphor::logging::entry("IMAGESIZE=0x%x", ++ static_cast<int>(size))); ++ ++ // Adds all digest algorithms to the internal table ++ OpenSSL_add_all_digests(); ++ ++ ctx = EVP_MD_CTX_create(); ++ EVP_DigestInit(ctx, EVP_sha256()); ++ ++ // Hash the image file and update the digest ++ auto dataPtr = mapFile(imgPath, size); ++ ++ EVP_DigestUpdate(ctx, ((uint8_t*)dataPtr() + lengthBlk0Blk1), ++ (size - lengthBlk0Blk1)); ++ ++ std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256())); ++ std::vector<uint8_t> expectedDigest(block0Data.hash256, ++ &block0Data.hash256[0] + 32); ++ ++ EVP_DigestFinal(ctx, digest.data(), &hashLen); ++ EVP_MD_CTX_destroy(ctx); ++ ++ std::string redfishMsgID = "OpenBMC.0.1"; ++ ++ if (expectedDigest != digest) ++ { ++ redfishMsgID += ".GeneralFirmwareSecurityViolation"; ++ sd_journal_send("MESSAGE=%s", ++ "Firmware image HASH verification failed", ++ "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s", ++ redfishMsgID.c_str(), "REDFISH_MESSAGE_ARGS=%s", ++ "Image HASH check fail", NULL); ++ return -1; ++ } ++ + phosphor::logging::log<phosphor::logging::level::INFO>( + "PFR image", + phosphor::logging::entry("PCType=%d", block0Data.pcType[0]), + phosphor::logging::entry("VERSION=%s", version.c_str())); ++ ++ version = ++ std::to_string(verData[0]) + "." + std::to_string(verData[1]); + } + catch (std::exception& e) + { +@@ -80,20 +176,21 @@ static int getPFRImgInfo(const std::filesystem::path imgPath, uint8_t& imgType, + + int Manager::processImage(const std::string& imgFilePath) + { ++ + std::filesystem::path imgPath(imgFilePath); + + if (!std::filesystem::exists(imgPath)) + return -1; + +- uint8_t imgType; + int retry = 3; + std::string ver; + std::string purposeString; + +- if (0 != getPFRImgInfo(imgFilePath, imgType, ver)) ++ if (0 != verifyPFRImage(imgFilePath, ver, purposeString)) + { + phosphor::logging::log<phosphor::logging::level::ERR>( +- "Error reading uploaded image type and version"); ++ "Error verifying uploaded image"); ++ std::filesystem::remove_all(imgFilePath); + return -1; + } + +@@ -104,31 +201,6 @@ int Manager::processImage(const std::string& imgFilePath) + return -1; + } + +- if (imgType == pfrBMCUpdateCap) +- { +- purposeString = +- "xyz.openbmc_project.Software.Version.VersionPurpose.BMC"; +- } +- else if (imgType == pfrPCHUpdateCap) +- { +- purposeString = +- "xyz.openbmc_project.Software.Version.VersionPurpose.Host"; +- } +- else if (imgType == pfrCPLDUpdateCap) +- { +- purposeString = +- "xyz.openbmc_project.Software.Version.VersionPurpose.Other"; +- } +- else +- { +- purposeString = +- "xyz.openbmc_project.Software.Version.VersionPurpose.Unknown"; +- +- phosphor::logging::log<phosphor::logging::level::ERR>( +- "Unknown image type"); +- return -1; +- } +- + sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose + purpose = Version::VersionPurpose::Unknown; + try +@@ -170,6 +242,7 @@ int Manager::processImage(const std::string& imgFilePath) + std::filesystem::create_directory(imageDirPath); + + std::filesystem::path newFileName = imageDirPath / "image-runtime"; ++ + std::filesystem::rename(imgFilePath, newFileName); + + // Create Version object +@@ -213,6 +286,14 @@ void Manager::erase(std::string entryId) + this->versions.erase(entryId); + } + ++CustomMap Manager::mapFile(const std::filesystem::path& path, size_t size) ++{ ++ ++ CustomFd fd(open(path.c_str(), O_RDONLY)); ++ ++ return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0), ++ size); ++} + } // namespace manager + } // namespace software + } // namespace phosphor +diff --git a/pfr_image_manager.hpp b/pfr_image_manager.hpp +index c6ee6a4..5b7b2c3 100644 +--- a/pfr_image_manager.hpp ++++ b/pfr_image_manager.hpp +@@ -1,8 +1,16 @@ + #pragma once + #include "version.hpp" + ++#include <openssl/evp.h> ++#include <openssl/pem.h> ++#include <openssl/rsa.h> ++#include <sys/mman.h> ++#include <unistd.h> ++ + #include <sdbusplus/server.hpp> + ++#include <filesystem> ++ + namespace phosphor + { + namespace software +@@ -22,7 +30,7 @@ enum pfrImgPCType + /* PFR image block 0 - As defined in HAS */ + struct pfrImgBlock0 + { +- uint8_t tag[4]; ++ uint32_t tag; + uint8_t pcLength[4]; + uint8_t pcType[4]; + uint8_t reserved1[4]; +@@ -31,6 +39,82 @@ struct pfrImgBlock0 + uint8_t reserved2[32]; + } __attribute__((packed)); + ++/** @struct CustomFd ++ * ++ * RAII wrapper for file descriptor. ++ */ ++struct CustomFd ++{ ++ public: ++ CustomFd() = delete; ++ CustomFd(const CustomFd&) = delete; ++ CustomFd& operator=(const CustomFd&) = delete; ++ CustomFd(CustomFd&&) = default; ++ CustomFd& operator=(CustomFd&&) = default; ++ /** @brief Saves File descriptor and uses it to do file operation ++ * ++ * @param[in] fd - File descriptor ++ */ ++ CustomFd(int fd) : fd(fd) ++ {} ++ ++ ~CustomFd() ++ { ++ if (fd >= 0) ++ { ++ close(fd); ++ } ++ } ++ ++ int operator()() const ++ { ++ return fd; ++ } ++ ++ private: ++ /** @brief File descriptor */ ++ int fd = -1; ++}; ++ ++/** @struct CustomMap ++ * ++ * RAII wrapper for mmap. ++ */ ++struct CustomMap ++{ ++ private: ++ /** @brief starting address of the map */ ++ void* addr; ++ ++ /** @brief length of the mapping */ ++ size_t length; ++ ++ public: ++ CustomMap() = delete; ++ CustomMap(const CustomMap&) = delete; ++ CustomMap& operator=(const CustomMap&) = delete; ++ CustomMap(CustomMap&&) = default; ++ CustomMap& operator=(CustomMap&&) = default; ++ ++ /** @brief Saves starting address of the map and ++ * and length of the file. ++ * @param[in] addr - Starting address of the map ++ * @param[in] length - length of the map ++ */ ++ CustomMap(void* addr, size_t length) : addr(addr), length(length) ++ {} ++ ++ ~CustomMap() ++ { ++ munmap(addr, length); ++ } ++ ++ void* operator()() const ++ { ++ return addr; ++ } ++}; ++ + /** @class Manager + * @brief Contains a map of Version dbus objects. + * @details The software image manager class that contains the Version dbus +@@ -63,6 +147,22 @@ class Manager + void erase(std::string entryId); + + private: ++ /** ++ * @brief Memory map the file ++ * @param[in] - file path ++ * @param[in] - file size ++ * @param[out] - Custom Mmap address ++ */ ++ CustomMap mapFile(const std::filesystem::path& path, size_t size); ++ ++ /** ++ * @brief Verify the PFR image and return version and purpose ++ * @param[in] - file path ++ * @param[out] - version ++ * @param[out] - purpose ++ */ ++ int verifyPFRImage(const std::filesystem::path imgPath, ++ std::string& version, std::string& purposeString); + /** @brief Persistent map of Version dbus objects and their + * version id */ + std::map<std::string, std::unique_ptr<Version>> versions; diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0008-item_updater-update-the-bmc_active-objectPath.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0008-item_updater-update-the-bmc_active-objectPath.patch new file mode 100644 index 000000000..e0ff79795 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0008-item_updater-update-the-bmc_active-objectPath.patch @@ -0,0 +1,50 @@ +From d9e50ecf8bd8bc764838e7244084184644a3f0fc Mon Sep 17 00:00:00 2001 +From: Chalapathi <chalapathix.venkataramashetty@intel.com> +Date: Thu, 23 Apr 2020 19:06:19 +0000 +Subject: [PATCH] item_updater: update the bmc_active objectPath + +Update the Software object path to bmc_active instead of random Id. + +Signed-off-by: Chalapathi <chalapathix.venkataramashetty@intel.com> + +--- + item_updater.cpp | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/item_updater.cpp b/item_updater.cpp +index e6dd298..c3a846d 100644 +--- a/item_updater.cpp ++++ b/item_updater.cpp +@@ -175,7 +175,8 @@ void ItemUpdater::processBMCImage() + if (0 == + iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX)) + { +- // Get the version to calculate the id ++ std::string id = "bmc_active"; ++ // upstream changed this to relative_path ... is that right? + fs::path releaseFile(OS_RELEASE_FILE); + auto osRelease = iter.path() / releaseFile.relative_path(); + if (!fs::is_regular_file(osRelease)) +@@ -189,7 +190,6 @@ void ItemUpdater::processBMCImage() + // volumes created by the UBI layout for example have the id in + // the mount directory name. The worst that can happen is that + // erase() is called with an non-existent id and returns. +- auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); + ItemUpdater::erase(id); + + continue; +@@ -203,14 +203,11 @@ void ItemUpdater::processBMCImage() + + // Try to delete the version, same as above if the + // OS_RELEASE_FILE does not exist. +- auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); + ItemUpdater::erase(id); + + continue; + } + +- auto id = VersionClass::getId(version); +- + // Check if the id has already been added. This can happen if the + // BMC partitions / devices were manually flashed with the same + // image. diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0009-Add-ApplyOptions-D-bus-property-under-Software.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0009-Add-ApplyOptions-D-bus-property-under-Software.patch new file mode 100644 index 000000000..f150c1027 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0009-Add-ApplyOptions-D-bus-property-under-Software.patch @@ -0,0 +1,44 @@ +From 76f169e71be10b50b9617e606c38aff9553e6de8 Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@intel.com> +Date: Fri, 15 May 2020 21:17:08 +0530 +Subject: [PATCH] Add ApplyOptions D-bus property under Software + +This change adds the ApplyOptions D-bus property +under xyz.openbmc_project.Software.BMC.Updater. +ApplyOptions is needed for BIOS NVRAM clear during +BIOS firmware update. ClearConfig attribute is passed +from RF to fwupd script. + +Tested: Set and Get of ClearConfig from fwupd.sh works + fine. + +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> +--- + item_updater.hpp | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/item_updater.hpp b/item_updater.hpp +index 3f0530f..5c1a779 100644 +--- a/item_updater.hpp ++++ b/item_updater.hpp +@@ -9,6 +9,7 @@ + #include <xyz/openbmc_project/Association/Definitions/server.hpp> + #include <xyz/openbmc_project/Common/FactoryReset/server.hpp> + #include <xyz/openbmc_project/Control/FieldMode/server.hpp> ++#include <xyz/openbmc_project/Software/ApplyOptions/server.hpp> + + #include <string> + #include <vector> +@@ -24,7 +25,8 @@ using ItemUpdaterInherit = sdbusplus::server::object::object< + sdbusplus::xyz::openbmc_project::Common::server::FactoryReset, + sdbusplus::xyz::openbmc_project::Control::server::FieldMode, + sdbusplus::xyz::openbmc_project::Association::server::Definitions, +- sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll>; ++ sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll, ++ sdbusplus::xyz::openbmc_project::Software::server::ApplyOptions>; + + namespace MatchRules = sdbusplus::bus::match::rules; + using VersionClass = phosphor::software::manager::Version; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0010-Add-error-reporting-to-pfr_image_manager.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0010-Add-error-reporting-to-pfr_image_manager.patch new file mode 100644 index 000000000..e72398efd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0010-Add-error-reporting-to-pfr_image_manager.patch @@ -0,0 +1,190 @@ +From ffa3642e436b559d8062f777f00458cc7b5ecb01 Mon Sep 17 00:00:00 2001 +From: James Feist <james.feist@linux.intel.com> +Date: Thu, 11 Jun 2020 13:30:02 -0700 +Subject: [PATCH 1/1] Add error reporting to pfr_image_manager + +This uses report functionality to update error +return status for redfish updates. + +Tested: Got 400 error with different messages based +on failure type + +{ + "error": { + "@Message.ExtendedInfo": [ + { + "@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message", + "Message": "Invalid file uploaded to /redfish/v1/UpdateService: invalid archive.", + "MessageArgs": [ + "/redfish/v1/UpdateService", + "invalid archive" + ], + "MessageId": "OpenBMC.0.1.0.InvalidFile", + "Resolution": "None.", + "Severity": "Warning" + } + ], + "code": "OpenBMC.0.1.0.InvalidFile", + "message": "Invalid file uploaded to /redfish/v1/UpdateService: invalid archive." + } +} + +{ + "error": { + "@Message.ExtendedInfo": [ + { + "@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message", + "Message": "Invalid file uploaded to /redfish/v1/UpdateService: invalid image format.", + "MessageArgs": [ + "/redfish/v1/UpdateService", + "invalid image format" + ], + "MessageId": "OpenBMC.0.1.0.InvalidFile", + "Resolution": "None.", + "Severity": "Warning" + } + ], + "code": "OpenBMC.0.1.0.InvalidFile", + "message": "Invalid file uploaded to /redfish/v1/UpdateService: invalid image format." + } +} + +{ + "error": { + "@Message.ExtendedInfo": [ + { + "@odata.type": "#Message.v1_0_0.Message", + "Message": "The resource /redfish/v1/UpdateService was unable to satisfy the request due to unavailability of resources.", + "MessageArgs": [ + "/redfish/v1/UpdateService" + ], + "MessageId": "Base.1.4.0.ResourceExhaustion", + "Resolution": "Ensure that the resources are available and resubmit the request.", + "Severity": "Critical" + } + ], + "code": "Base.1.4.0.ResourceExhaustion", + "message": "The resource /redfish/v1/UpdateService was unable to satisfy the request due to unavailability of resources." + } +} + +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + dbus_helpers.hpp | 30 ++++++++++++++++++++++++++++++ + pfr_image_manager.cpp | 18 ++++++++++++++++++ + 2 files changed, 48 insertions(+) + create mode 100644 dbus_helpers.hpp + +diff --git a/dbus_helpers.hpp b/dbus_helpers.hpp +new file mode 100644 +index 0000000..b9ffa36 +--- /dev/null ++++ b/dbus_helpers.hpp +@@ -0,0 +1,30 @@ ++#pragma once ++ ++#include "config.h" ++ ++#include <sdbusplus/bus.hpp> ++inline bool isFwupdScriptRunning(sdbusplus::bus::bus& bus) ++{ ++ using ObjectPath = sdbusplus::message::object_path; ++ // type is ssssssouso ++ using ListUnitsType = ++ std::tuple<std::string, std::string, std::string, std::string, ++ std::string, std::string, ObjectPath, uint32_t, std::string, ++ ObjectPath>; ++ auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, ++ SYSTEMD_INTERFACE, "ListUnits"); ++ ++ auto reply = bus.call(method); ++ std::vector<ListUnitsType> resp; ++ reply.read(resp); ++ ++ for (const auto& unit : resp) ++ { ++ if (std::get<0>(unit).find("fwupd@") != std::string::npos && ++ std::get<3>(unit) != "failed") ++ { ++ return true; ++ } ++ } ++ return false; ++} +diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp +index 1a41cbe..fe1e6f9 100644 +--- a/pfr_image_manager.cpp ++++ b/pfr_image_manager.cpp +@@ -2,6 +2,7 @@ + + #include "pfr_image_manager.hpp" + ++#include "dbus_helpers.hpp" + #include "version.hpp" + #include "watch.hpp" + +@@ -33,6 +34,9 @@ namespace manager + + using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error; + namespace Software = phosphor::logging::xyz::openbmc_project::Software; ++using UnTarFail = Software::Image::UnTarFailure; ++using ImageFail = Software::Image::ImageFailure; ++using BusyFail = Software::Image::BusyFailure; + + static constexpr const uint32_t pfmPos = 2054; + static constexpr const uint32_t block0Magic = 0xB6EAFD19; +@@ -76,6 +80,8 @@ int Manager::verifyPFRImage(const std::filesystem::path imgPath, + phosphor::logging::log<phosphor::logging::level::ERR>( + "Image magic number match failed", + phosphor::logging::entry("IMAGEMAGIC=0x%x", imgMagic)); ++ phosphor::logging::report<UnTarFailure>( ++ UnTarFail::PATH(imgPath.c_str())); + return -1; + } + +@@ -110,6 +116,9 @@ int Manager::verifyPFRImage(const std::filesystem::path imgPath, + + phosphor::logging::log<phosphor::logging::level::ERR>( + "Unknown image type"); ++ phosphor::logging::report<ImageFailure>( ++ ImageFail::FAIL("Unknown image type"), ++ ImageFail::PATH(imgPath.c_str())); + return -1; + } + +@@ -153,6 +162,9 @@ int Manager::verifyPFRImage(const std::filesystem::path imgPath, + "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s", + redfishMsgID.c_str(), "REDFISH_MESSAGE_ARGS=%s", + "Image HASH check fail", NULL); ++ phosphor::logging::report<ImageFailure>( ++ ImageFail::FAIL("Security violation: hash mismatch"), ++ ImageFail::PATH(imgPath.c_str())); + return -1; + } + +@@ -167,6 +179,9 @@ int Manager::verifyPFRImage(const std::filesystem::path imgPath, + catch (std::exception& e) + { + phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); ++ phosphor::logging::report<ImageFailure>( ++ ImageFail::FAIL("Unhandled exception"), ++ ImageFail::PATH(imgPath.c_str())); + return -1; + } + } +@@ -182,6 +197,12 @@ int Manager::processImage(const std::string& imgFilePath) + if (!std::filesystem::exists(imgPath)) + return -1; + ++ if (isFwupdScriptRunning(bus)) ++ { ++ phosphor::logging::report<BusyFailure>(BusyFail::PATH(imgPath.c_str())); ++ return -1; ++ } ++ + int retry = 3; + std::string ver; + std::string purposeString; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0013-remove-image-file-on-pre-script-failures.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0013-remove-image-file-on-pre-script-failures.patch new file mode 100644 index 000000000..6bbd59918 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0013-remove-image-file-on-pre-script-failures.patch @@ -0,0 +1,40 @@ +From 0628db177655e6f890c4da8c7de7c3cd7487d528 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Thu, 16 Jul 2020 14:16:28 -0700 +Subject: [PATCH] remove image file on pre-script failures + +Multiple back-to-back updates of bad images will cause the software +manager to leave junk images hanging around. This is part of a fix that +will remove them if the software manager never gets around to launching +the fwupd.sh script. The other part is that the fwupd.sh script must +always delete the image file on exit, success or failure. + +Tested: posted a garbage file, saw that it was deleted even though + fwupd.sh was never invoked. + +Change-Id: I6b049916a3edcb48f9d4ebe0d4715b94214b4feb +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +--- + watch.cpp | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/watch.cpp b/watch.cpp +index d6c09a946fd0..5d70edbf52b4 100644 +--- a/watch.cpp ++++ b/watch.cpp +@@ -106,6 +106,12 @@ int Watch::callback(sd_event_source* /* s */, int fd, uint32_t revents, + { + error("Error ({RC}) processing image {IMAGE}", "RC", rc, + "IMAGE", tarballPath); ++ std::error_code ec{}; ++ fs::remove_all(tarballPath, ec); ++ if (!ec) ++ { ++ error("Unable to remove image on processing failure"); ++ } + } + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0014-PFR-image-verification.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0014-PFR-image-verification.patch new file mode 100644 index 000000000..dfc7f2e58 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0014-PFR-image-verification.patch @@ -0,0 +1,116 @@ +From 9d82d53b50769506926dd99273f197a268d68fa3 Mon Sep 17 00:00:00 2001 +From: Chalapathi Venkataramashetty <chalapathix.venkataramashetty@intel.com> +Date: Thu, 30 Jul 2020 09:50:40 +0000 +Subject: [PATCH] PFR-image-verification + +Add support verify the complete fw image by using mtd-util repo's +pfr_authenticate function. + +Tested. +1. Upload the corrupted image. +POST: https://<BMC_IP>/redfish/v1/UpdateService/ + with <Corrupted BMC_signed_cap> binary file +Response: +{ + "error": { + "@Message.ExtendedInfo": [ + { + "@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message", + "Message": "Invalid file uploaded to /redfish/v1/UpdateService: + Invalid image format.", + "MessageArgs": [ + "/redfish/v1/UpdateService", + "Invalid image format" + ], + "MessageId": "OpenBMC.0.1.0.InvalidUpload", + "Resolution": "None.", + "Severity": "Warning" + } + ], + "code": "OpenBMC.0.1.0.InvalidUpload", + "message": "Invalid file uploaded to /redfish/v1/UpdateService: + Invalid image format." + } +} + +2. Upload the correct image. +POST: https://<BMC_IP>/redfish/v1/UpdateService/ + with <BMC_signed_cap> binary file + +Image verified and firmware updated. +{ + "@odata.id": "/redfish/v1/TaskService/Tasks/0", + "@odata.type": "#Task.v1_4_3.Task", + "Id": "0", + "TaskState": "Running", + "TaskStatus": "OK" +} + +Command: +GET: https://<BMC_IP>/redfish/v1/Systems/system/LogServices/EventLog/ + Entries + +Response: +{ + "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/ + Entries/1596082187", + "@odata.type": "#LogEntry.v1_4_0.LogEntry", + "Created": "2020-07-30T04:09:47+00:00", + "EntryType": "Event", + "Id": "1596082187", + "Message": "BMC firmware update to version 00.72 completed + successfully.", + "MessageArgs": [ + "BMC", + "00.72" + ], + "MessageId": "OpenBMC.0.1.FirmwareUpdateCompleted", + "Name": "System Event Log Entry", + "Severity": "OK" + }, + +Signed-off-by: Chalapathi Venkataramashetty <chalapathix.venkataramashetty@intel.com> +--- + pfr_image_manager.cpp | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp +index eeed4fe..16231fa 100644 +--- a/pfr_image_manager.cpp ++++ b/pfr_image_manager.cpp +@@ -15,6 +15,7 @@ + #include <time.h> + #include <unistd.h> + ++#include <boost/process/child.hpp> + #include <elog-errors.hpp> + #include <xyz/openbmc_project/Software/Image/error.hpp> + +@@ -122,6 +123,24 @@ int Manager::verifyPFRImage(const std::filesystem::path imgPath, + return -1; + } + ++ // Verify the complete image ++ std::string mtdUtilfile = "/usr/bin/mtd-util"; ++ std::vector<std::string> mtdUtilCmd = {"p", "a"}; ++ mtdUtilCmd.push_back(imgPath); ++ ++ boost::process::child execProg(mtdUtilfile, mtdUtilCmd); ++ execProg.wait(); ++ if (execProg.exit_code()) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Image authentication failed"); ++ phosphor::logging::report<ImageFailure>( ++ ImageFail::FAIL( ++ "Security violation: image authentication failure"), ++ ImageFail::PATH(imgPath.c_str())); ++ return -1; ++ } ++ + imgFile.seekg(pfmPos, + std::ios::beg); // Version is at 0x806 in the PFM + imgFile.read(reinterpret_cast<char*>(&verData), sizeof(verData)); +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0016-Process-PLDM-image-type.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0016-Process-PLDM-image-type.patch new file mode 100644 index 000000000..bc94f00af --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0016-Process-PLDM-image-type.patch @@ -0,0 +1,234 @@ +From a78b7a609f58ac82623c357426ef0590d6d76971 Mon Sep 17 00:00:00 2001 +From: Ayushi Smriti <smriti.ayushi@intel.com> +Date: Mon, 9 Nov 2020 23:04:58 +0530 +Subject: [PATCH] Process PLDM image type + +This change is to check whether the image uploaded is of PLDM image +type based on the PackageHeaderIdentifier check which is a 16 bytes +uuid field in the pldm package header. + +Also, determine image purpose and version. +Purpose is set to pldm enum type and for version, PackageVersionString +is concluded based on PackageVersionStringLength value. + +Tested: +- On uploading a pldm image through Redfish. Uuid is identified and +matched correctly. +- Purpose and version is given to the image as expected and activation +intf got added. + - verified same with busctl cmd on xyz.openbmc_project.Software.Version + and xyz.openbmc_project.Software.BMC.Updater +- Verified the regular PFR update procedure works + - received expected redfish response from postman + - verified fwupd.sh script is reached + +Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com> +--- + item_updater.cpp | 4 +- + pfr_image_manager.cpp | 95 +++++++++++++++++++++++++++++++++++++++++-- + pfr_image_manager.hpp | 6 +-- + pldm.hpp | 21 ++++++++++ + 4 files changed, 119 insertions(+), 7 deletions(-) + create mode 100644 pldm.hpp + +diff --git a/item_updater.cpp b/item_updater.cpp +index db255d6..7af80e3 100644 +--- a/item_updater.cpp ++++ b/item_updater.cpp +@@ -67,6 +67,7 @@ void ItemUpdater::createActivation(sdbusplus::message::message& msg) + #if defined(HOST_BIOS_UPGRADE) || defined(PFR_UPDATE) + value == VersionPurpose::Host || + #endif ++ value == VersionPurpose::PLDM || + value == VersionPurpose::Other) + { + purpose = value; +@@ -397,7 +398,8 @@ void ItemUpdater::deleteAll() + } + + ItemUpdater::ActivationStatus +- ItemUpdater::validateSquashFSImage(const std::string& filePath) ++ ItemUpdater::validateSquashFSImage(__attribute__((unused)) ++ const std::string& filePath) + { + #ifndef PFR_UPDATE + bool valid = true; +diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp +index 178367f..c923494 100644 +--- a/pfr_image_manager.cpp ++++ b/pfr_image_manager.cpp +@@ -3,6 +3,7 @@ + #include "pfr_image_manager.hpp" + + #include "dbus_helpers.hpp" ++#include "pldm.hpp" + #include "version.hpp" + #include "watch.hpp" + +@@ -44,9 +45,10 @@ using BusyFail = Software::Image::BusyFailure; + static constexpr const uint32_t pfmPos = 2054; + static constexpr const uint32_t block0Magic = 0xB6EAFD19; + static constexpr const uint32_t lengthBlk0Blk1 = 1024; ++static constexpr const uint32_t pldmMagic = 0xF018878C; + +-int Manager::verifyPFRImage(const std::filesystem::path imgPath, +- std::string& version, std::string& purposeString) ++int Manager::verifyImage(const std::filesystem::path imgPath, ++ std::string& version, std::string& purposeString) + { + uint8_t imgType = 0; + uint32_t imgMagic = 0; +@@ -76,6 +78,93 @@ int Manager::verifyPFRImage(const std::filesystem::path imgPath, + + imgMagic = block0Data.tag; + ++ if (htobe32(imgMagic) == pldmMagic) ++ { ++ if (!version.empty()) ++ { ++ version.clear(); ++ } ++ ++ imgFile.seekg(0, std::ios_base::end); ++ ++ const size_t length = imgFile.tellg(); ++ constexpr size_t readBytes = 36; ++ ++ if (length < readBytes) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Insufficient file length to read the required " ++ "bytes"); ++ return -1; ++ } ++ ++ imgFile.seekg(0, std::ios::beg); ++ ++ std::array<char, readBytes> buffer = {}; ++ ++ imgFile.read( ++ buffer.data(), ++ buffer.size()); // read 36 bytes of PLDM Package Header ++ ++ if (!imgFile.good()) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Image file read is not successful"); ++ return -1; ++ } ++ ++ if (!std::equal(buffer.begin(), ++ buffer.begin() + pldm::headerIdLen, ++ pldm::pldmPkgHeaderId ++ .begin())) // comparing 16 bytes of ++ // PackageHeaderIdentifier field ++ { ++ std::string redfishMsgID = ++ "OpenBMC.0.1.FirmwareUpdateFailed"; ++ sd_journal_send( ++ "MESSAGE=%s", "Firmware image verification failed", ++ "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s", ++ redfishMsgID.c_str(), "REDFISH_MESSAGE_ARGS=%s", ++ "PLDM Image package header identifier check fail", ++ NULL); ++ ++ return -1; ++ } ++ ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "Package header identifier matched"); ++ purposeString = ++ "xyz.openbmc_project.Software.Version.VersionPurpose.PLDM"; ++ ++ const uint8_t pkgVerStrLen = static_cast<uint8_t>( ++ buffer[35]); // PackageVersionStringLen byte ++ ++ imgFile.seekg(readBytes, ++ std::ios::beg); // point to the begin of ++ // PackageVersionString field ++ // i.e. 36th pos ++ ++ std::array<char, 255> ver = {}; ++ imgFile.read(ver.data(), ++ pkgVerStrLen); // read PackageVersionString bytes ++ ++ if (!imgFile.good()) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Image file read is not successful"); ++ return -1; ++ } ++ ++ version.assign(ver.data(), pkgVerStrLen); ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "Package version string value", ++ phosphor::logging::entry("IMAGE_VERSION=%s", ++ version.c_str())); ++ ++ imgFile.close(); ++ return 0; ++ } ++ + if (imgMagic != block0Magic) + { + phosphor::logging::log<phosphor::logging::level::ERR>( +@@ -226,7 +315,7 @@ int Manager::processImage(const std::string& imgFilePath) + std::string ver; + std::string purposeString; + +- if (0 != verifyPFRImage(imgFilePath, ver, purposeString)) ++ if (0 != verifyImage(imgFilePath, ver, purposeString)) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Error verifying uploaded image"); +diff --git a/pfr_image_manager.hpp b/pfr_image_manager.hpp +index 3591f1a..2facfe6 100644 +--- a/pfr_image_manager.hpp ++++ b/pfr_image_manager.hpp +@@ -156,13 +156,13 @@ class Manager + CustomMap mapFile(const std::filesystem::path& path, size_t size); + + /** +- * @brief Verify the PFR image and return version and purpose ++ * @brief Verify the uploaded image type and return version and purpose + * @param[in] - file path + * @param[out] - version + * @param[out] - purpose + */ +- int verifyPFRImage(const std::filesystem::path imgPath, +- std::string& version, std::string& purposeString); ++ int verifyImage(const std::filesystem::path imgPath, std::string& version, ++ std::string& purposeString); + /** @brief Persistent map of Version dbus objects and their + * version id */ + std::map<std::string, std::unique_ptr<Version>> versions; +diff --git a/pldm.hpp b/pldm.hpp +new file mode 100644 +index 0000000..edbd6ae +--- /dev/null ++++ b/pldm.hpp +@@ -0,0 +1,21 @@ ++namespace pldm ++{ ++ ++struct PldmPkgHeader ++{ ++ uint8_t uuid[16]; // PackageHeaderIdentifier ++ uint8_t formatRev; // PackageHeaderFormatRevision ++ uint16_t headerSize; // PackageHeaderSize ++ uint8_t timestamp[13]; // PackageReleaseDateTime ++ uint16_t bitmapLen; // ComponentBitmapBitLength ++ uint8_t verStringType; // PackageVersionStringType ++ uint8_t verStringLen; // PackageVersionStringLength ++} __attribute__((packed)); ++ ++constexpr size_t headerIdLen = 16; ++ ++const std::array<char, headerIdLen> pldmPkgHeaderId = { ++ 0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43, ++ 0x98, 0x00, 0xA0, 0x2F, 0x05, 0x9A, 0xCA, 0x02}; // 16 bytes package header ++ // identifier uuid ++} // namespace pldm +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0017-Fix-build-error.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0017-Fix-build-error.patch new file mode 100644 index 000000000..d21dbd63c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0017-Fix-build-error.patch @@ -0,0 +1,34 @@ +From 1f3531eff8a05bb5375dea89c1ca9292f69863b0 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 11 Mar 2021 11:42:39 -0800 +Subject: [PATCH] Fix build error + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + pfr_image_manager.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp +index f844b8e79565..55ad21f8a3d4 100644 +--- a/pfr_image_manager.cpp ++++ b/pfr_image_manager.cpp +@@ -333,6 +333,7 @@ int Manager::processImage(const std::string& imgFilePath) + + int retry = 3; + std::string ver; ++ std::string extVer; + std::string purposeString; + + if (0 != verifyImage(imgFilePath, ver, purposeString)) +@@ -398,7 +399,7 @@ int Manager::processImage(const std::string& imgFilePath) + std::string objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; + + auto versionPtr = std::make_unique<Version>( +- bus, objPath, ver, purpose, imageDirPath.string(), ++ bus, objPath, ver, purpose, extVer, imageDirPath.string(), + std::bind(&Manager::erase, this, std::placeholders::_1)); + versionPtr->deleteObject = + std::make_unique<phosphor::software::manager::Delete>(bus, objPath, +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0018-Fix-delete-image-by-ID-and-inhibit-removal-of-bmc_ac.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0018-Fix-delete-image-by-ID-and-inhibit-removal-of-bmc_ac.patch new file mode 100644 index 000000000..0b2026c92 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0018-Fix-delete-image-by-ID-and-inhibit-removal-of-bmc_ac.patch @@ -0,0 +1,171 @@ +From 77b861136b6780ce4eabfe9589a0b584e6ed2b43 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Wed, 21 Apr 2021 21:16:47 +0000 +Subject: [PATCH] Fix delete image by ID and inhibit removal of bmc_active + +Delete image by ID was broken because when hitting the delete dbus +interface, it recalculated the ID from the parent version, which then +does not match because of the random number addition that was added +to the ID when the parent interface was created. This saves away the +parent interface ID and recalls it rather than recalculating it. + +Also, there was a logic error in deleting images that would delete the +active BMC image. This fixes up that error. + +Tested: run multiple back-to back updates and see that when the fwupd + script calls delete, the interfaces are deleted and that the + bmc_active interface is not deleted. + +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +--- + image_manager.cpp | 2 +- + item_updater.cpp | 16 +++++++++++----- + pfr_image_manager.cpp | 2 +- + version.cpp | 2 +- + version.hpp | 19 +++++++++++++++---- + 5 files changed, 29 insertions(+), 12 deletions(-) + +diff --git a/image_manager.cpp b/image_manager.cpp +index 6334704cd980..4fefd221e6d2 100644 +--- a/image_manager.cpp ++++ b/image_manager.cpp +@@ -221,7 +221,7 @@ int Manager::processImage(const std::string& tarFilePath) + { + // Create Version object + auto versionPtr = std::make_unique<Version>( +- bus, objPath, version, purpose, extendedVersion, ++ bus, objPath, id, version, purpose, extendedVersion, + imageDirPath.string(), + std::bind(&Manager::erase, this, std::placeholders::_1)); + versionPtr->deleteObject = +diff --git a/item_updater.cpp b/item_updater.cpp +index 26b52b3f7846..3f64feb43c55 100644 +--- a/item_updater.cpp ++++ b/item_updater.cpp +@@ -145,7 +145,7 @@ void ItemUpdater::createActivation(sdbusplus::message::message& msg) + activationState, associations))); + + auto versionPtr = std::make_unique<VersionClass>( +- bus, path, version, purpose, extendedVersion, filePath, ++ bus, path, versionId, version, purpose, extendedVersion, filePath, + std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); + versionPtr->deleteObject = + std::make_unique<phosphor::software::manager::Delete>(bus, path, +@@ -260,7 +260,7 @@ void ItemUpdater::processBMCImage() + + // Create Version instance for this version. + auto versionPtr = std::make_unique<VersionClass>( +- bus, path, version, purpose, extendedVersion, "", ++ bus, path, id, version, purpose, extendedVersion, "", + std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); + auto isVersionFunctional = versionPtr->isFunctional(); + if (!isVersionFunctional) +@@ -336,9 +336,9 @@ void ItemUpdater::erase(std::string entryId) + auto it = versions.find(entryId); + if (it != versions.end()) + { +- if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1) ++ if (it->second->isFunctional()) + { +- error( ++ info( + "Version ({VERSIONID}) is currently running on the BMC; unable to remove.", + "VERSIONID", entryId); + return; +@@ -679,6 +679,12 @@ void ItemUpdater::freeSpace(Activation& caller) + std::size_t count = 0; + for (const auto& iter : activations) + { ++ if (versions.find(iter.second->versionId)->second->isFunctional()) ++ { ++ // don't bother with function versions ++ continue; ++ } ++ + if ((iter.second.get()->activation() == + server::Activation::Activations::Active) || + (iter.second.get()->activation() == +@@ -772,7 +778,7 @@ void ItemUpdater::createBIOSObject() + // Do nothing; + }; + biosVersion = std::make_unique<VersionClass>( +- bus, path, version, VersionPurpose::Host, "", "", ++ bus, path, versionId, version, VersionPurpose::Host, "", "", + std::bind(dummyErase, std::placeholders::_1)); + biosVersion->deleteObject = + std::make_unique<phosphor::software::manager::Delete>(bus, path, +diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp +index 80db63ca4d85..03bc34a3a78b 100644 +--- a/pfr_image_manager.cpp ++++ b/pfr_image_manager.cpp +@@ -399,7 +399,7 @@ int Manager::processImage(const std::string& imgFilePath) + std::string objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; + + auto versionPtr = std::make_unique<Version>( +- bus, objPath, ver, purpose, extVer, imageDirPath.string(), ++ bus, objPath, id, ver, purpose, extVer, imageDirPath.string(), + std::bind(&Manager::erase, this, std::placeholders::_1)); + versionPtr->deleteObject = + std::make_unique<phosphor::software::manager::Delete>(bus, objPath, +diff --git a/version.cpp b/version.cpp +index 97f3be94b4aa..5410c38887f8 100644 +--- a/version.cpp ++++ b/version.cpp +@@ -208,7 +208,7 @@ void Delete::delete_() + { + if (parent.eraseCallback) + { +- parent.eraseCallback(parent.getId(parent.version())); ++ parent.eraseCallback(parent.getExtId()); + } + } + +diff --git a/version.hpp b/version.hpp +index 8a68cb5f7b1f..afc589c0226c 100644 +--- a/version.hpp ++++ b/version.hpp +@@ -77,11 +77,11 @@ class Version : public VersionInherit + * @param[in] callback - The eraseFunc callback + */ + Version(sdbusplus::bus::bus& bus, const std::string& objPath, +- const std::string& versionString, VersionPurpose versionPurpose, +- const std::string& extVersion, const std::string& filePath, +- eraseFunc callback) : ++ const std::string& extId, const std::string& versionString, ++ VersionPurpose versionPurpose, const std::string& extVersion, ++ const std::string& filePath, eraseFunc callback) : + VersionInherit(bus, (objPath).c_str(), true), +- eraseCallback(callback), versionStr(versionString) ++ eraseCallback(callback), versionStr(versionString), extId(extId) + { + // Set properties. + extendedVersion(extVersion); +@@ -150,6 +150,15 @@ class Version : public VersionInherit + */ + bool isFunctional(); + ++ /* @brief Return the extended ID of this version object ++ * ++ * @ return - returns the extended ID string ++ */ ++ std::string getExtId() ++ { ++ return extId; ++ } ++ + /** @brief Persistent Delete D-Bus object */ + std::unique_ptr<Delete> deleteObject; + +@@ -159,6 +168,8 @@ class Version : public VersionInherit + private: + /** @brief This Version's version string */ + const std::string versionStr; ++ /** @brief This is extended version string */ ++ const std::string extId; + }; + + } // namespace manager +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0019-log-redfish-errors-on-all-pfr-image-auth-failures.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0019-log-redfish-errors-on-all-pfr-image-auth-failures.patch new file mode 100644 index 000000000..3a245c944 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0019-log-redfish-errors-on-all-pfr-image-auth-failures.patch @@ -0,0 +1,100 @@ +From 8d3eb2a57a70715b2cc6088904e8be007ab921b2 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Fri, 27 Aug 2021 11:44:02 -0700 +Subject: [PATCH 1/4] log redfish errors on all pfr image auth failures + +Previous code was doing a 'mtd-util pfr authenticate' prior to manually +calculating and comparing the hashes. This is incorrect behavior. There +is no need to manually calculate hashes since that is part of the pfr +authentication process. + +An unintended side effect of this is that if pfr authenticate fails for +some reason other than hash compare, the redfish log does not happen. + +Tested: phosphor-version-software-mananger logs a redfish log on rom id + mismatch as expected. + +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +--- + pfr_image_manager.cpp | 51 +++++-------------------------------------- + 1 file changed, 5 insertions(+), 46 deletions(-) + +diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp +index ce850cb..aa96a99 100644 +--- a/pfr_image_manager.cpp ++++ b/pfr_image_manager.cpp +@@ -53,13 +53,10 @@ int Manager::verifyImage(const std::filesystem::path imgPath, + uint8_t imgType = 0; + uint32_t imgMagic = 0; + uint8_t verData[2] = {0}; +- uint32_t hashLen = 0; + struct pfrImgBlock0 block0Data = {}; + + std::string imageName; + +- EVP_MD_CTX* ctx; +- + if (std::filesystem::exists(imgPath)) + { + try +@@ -227,52 +224,14 @@ int Manager::verifyImage(const std::filesystem::path imgPath, + ImageFail::FAIL( + "Security violation: image authentication failure"), + ImageFail::PATH(imgPath.c_str())); +- return -1; +- } +- +- imgFile.seekg(pfmPos, +- std::ios::beg); // Version is at 0x806 in the PFM +- imgFile.read(reinterpret_cast<char*>(&verData), sizeof(verData)); +- imgFile.close(); +- +- auto size = std::filesystem::file_size(imgPath); +- +- phosphor::logging::log<phosphor::logging::level::INFO>( +- "Image Size", phosphor::logging::entry("IMAGESIZE=0x%x", +- static_cast<int>(size))); +- +- // Adds all digest algorithms to the internal table +- OpenSSL_add_all_digests(); +- +- ctx = EVP_MD_CTX_create(); +- EVP_DigestInit(ctx, EVP_sha256()); + +- // Hash the image file and update the digest +- auto dataPtr = mapFile(imgPath, size); +- +- EVP_DigestUpdate(ctx, ((uint8_t*)dataPtr() + lengthBlk0Blk1), +- (size - lengthBlk0Blk1)); +- +- std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256())); +- std::vector<uint8_t> expectedDigest(block0Data.hash256, +- &block0Data.hash256[0] + 32); +- +- EVP_DigestFinal(ctx, digest.data(), &hashLen); +- EVP_MD_CTX_destroy(ctx); +- +- std::string redfishMsgID = "OpenBMC.0.1"; +- +- if (expectedDigest != digest) +- { +- redfishMsgID += ".GeneralFirmwareSecurityViolation"; ++ constexpr const char* redfishMsgID = ++ "OpenBMC.0.1.GeneralFirmwareSecurityViolation"; + sd_journal_send("MESSAGE=%s", +- "Firmware image HASH verification failed", ++ "Firmware image authentication failed", + "PRIORITY=%i", LOG_ERR, "REDFISH_MESSAGE_ID=%s", +- redfishMsgID.c_str(), "REDFISH_MESSAGE_ARGS=%s", +- "Image HASH check fail", NULL); +- phosphor::logging::report<ImageFailure>( +- ImageFail::FAIL("Security violation: hash mismatch"), +- ImageFail::PATH(imgPath.c_str())); ++ redfishMsgID, "REDFISH_MESSAGE_ARGS=%s", ++ "Image authentication check fail", NULL); + return -1; + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/fwupd@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/fwupd@.service new file mode 100644 index 000000000..d21647611 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/fwupd@.service @@ -0,0 +1,8 @@ +[Unit]
+Description=Flash BMC with fwupd script : %I
+
+[Service]
+Type=oneshot
+RemainAfterExit=no
+ExecStart=/bin/systemd-inhibit --what=shutdown:sleep --who=fwupd --why "Firmware Update %i" --mode=block /usr/bin/fwupd.sh %i
+SyslogIdentifier=fwupd
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend new file mode 100644 index 000000000..53d09387b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend @@ -0,0 +1,30 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" +EXTRA_OEMESON += "-Dfwupd-script=enabled" + +SYSTEMD_SERVICE:${PN}-updater += "fwupd@.service" + +EXTRA_OEMESON += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', '-Dpfr-update=enabled', '', d)}" + +SRC_URI += " \ + file://0002-Redfish-firmware-activation.patch \ + file://0004-Changed-the-condition-of-software-version-service-wa.patch \ + file://0005-Modified-firmware-activation-to-launch-fwupd.sh-thro.patch \ + file://0006-Modify-the-ID-of-software-image-updater-object-on-DB.patch \ + file://0007-Adding-StandBySpare-for-firmware-activation.patch \ + file://0008-item_updater-update-the-bmc_active-objectPath.patch \ + file://0009-Add-ApplyOptions-D-bus-property-under-Software.patch \ + file://0013-remove-image-file-on-pre-script-failures.patch \ + " + +SRC_URI_PFR = " \ + file://0007-PFR-images-support.patch \ + file://0008-PFR-image-HASH-verification.patch \ + file://0010-Add-error-reporting-to-pfr_image_manager.patch \ + file://0014-PFR-image-verification.patch \ + file://0016-Process-PLDM-image-type.patch \ + file://0017-Fix-build-error.patch \ + file://0018-Fix-delete-image-by-ID-and-inhibit-removal-of-bmc_ac.patch \ + file://0019-log-redfish-errors-on-all-pfr-image-auth-failures.patch \ + " + +SRC_URI += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', SRC_URI_PFR, '', d)}" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru.bb new file mode 100644 index 000000000..a8dd35487 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru.bb @@ -0,0 +1,26 @@ +SUMMARY = "Default Fru" +DESCRIPTION = "Builds a default FRU file at runtime based on board ID" + +inherit obmc-phosphor-systemd +inherit cmake + +SRC_URI = "file://checkFru.sh;subdir=${BP} \ + file://decodeBoardID.sh;subdir=${BP} \ + file://mkfru.cpp;subdir=${BP} \ + file://CMakeLists.txt;subdir=${BP} \ + " +SYSTEMD_SERVICE:${PN} = "SetBaseboardFru.service" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "\ + file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658 \ + file://mkfru.cpp;beginline=2;endline=14;md5=c451359f18a13ee69602afce1588c01a \ + " + +RDEPENDS:${PN} = "bash" + +do_install:append() { + install -d ${D}${bindir} + install -m 0755 ${S}/checkFru.sh ${D}${bindir}/checkFru.sh + install -m 0755 ${S}/decodeBoardID.sh ${D}${bindir}/decodeBoardID.sh +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/CMakeLists.txt new file mode 100644 index 000000000..a8e633644 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) +project(mkfru CXX) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +add_executable(mkfru mkfru.cpp) +install(TARGETS mkfru DESTINATION bin) + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/SetBaseboardFru.service b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/SetBaseboardFru.service new file mode 100644 index 000000000..d8c2a75ac --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/SetBaseboardFru.service @@ -0,0 +1,9 @@ +[Unit] +Description=Check for FRU presence + +[Service] +ExecStart=/usr/bin/checkFru.sh +Type=oneshot + +[Install] +WantedBy=basic.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/checkFru.sh b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/checkFru.sh new file mode 100755 index 000000000..18a6c7260 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/checkFru.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# this script checks the gpio id and loads the correct baseboard fru +FRUPATH="/etc/fru" +PRODIDPATH="/var/cache/private" +fruFile="$FRUPATH/baseboard.fru.bin" +prodIDFile="$PRODIDPATH/prodID" +source decodeBoardID.sh + +read_id() { + local idx=0 + local result=0 + local value=0 + for ((idx=0; idx<6; idx++)) + do + typeset -i value=$(gpioget $(gpiofind "FM_BMC_BOARD_SKU_ID${idx}_N")) + value=$((value << idx)) + result=$((result | value)) + done + echo $result +} + +if [ -f $fruFile -a -f $prodIDFile ] && + grep -q 'CPU part\s*: 0xc07' /proc/cpuinfo; then + exit 0 +fi + +NAME="Unknown" +PRODID="0x00" +EEPROM_FRU=false + +BOARD_ID=$(read_id) +decode_board_id + +if [ ! -e $prodIDFile ] +then + echo $PRODID >$prodIDFile +fi + +if $EEPROM_FRU; +then + # Remove baseboard filesystem FRU(if any), as this platform has EEPROM FRU. + rm -f $fruFile + exit 0 +fi + +if [ ! -f $fruFile ] +then + cd /tmp + mkdir -p $FRUPATH + mkfru $NAME + mv $NAME.fru.bin $fruFile +fi + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/decodeBoardID.sh b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/decodeBoardID.sh new file mode 100644 index 000000000..80710ae26 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/decodeBoardID.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# this script uses the BOARD_ID set from checkFru.sh and provides the NAME, +# PRODID, and EEPROM_FRU values for this platform +decode_board_id() { +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/mkfru.cpp b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/mkfru.cpp new file mode 100644 index 000000000..afadbd324 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/mkfru.cpp @@ -0,0 +1,219 @@ +/* +// Copyright (c) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Abstract: default FRU generation +// +*/ + +#include <fstream> +#include <iostream> +#include <iterator> +#include <numeric> +#include <string> +#include <vector> + +constexpr uint8_t fillChar = '.'; +constexpr uint8_t eof = 0xc1; +const std::string intel = "Intel Corporation"; + +// round up to nearest block size (power of 2) +constexpr size_t blockRound(size_t len, size_t blk) +{ + return ((len) + (((blk) - ((len) & ((blk)-1))) & ((blk)-1))); +} + +uint8_t mklen(uint8_t len) +{ + return static_cast<uint8_t>((0x3 << 6) | len); +} + +struct FruEntry +{ + static constexpr size_t fruBlockSize = 8; // type, length, checksum + static constexpr size_t fixedBytes = 3; // type, length, checksum + FruEntry() = delete; + FruEntry(const std::vector<uint8_t>& contents) + { + constexpr size_t verOffset = 0; + constexpr size_t lenOffset = 1; + value.resize(blockRound(fixedBytes + contents.size(), fruBlockSize)); + value[verOffset] = 1; + value[lenOffset] = blocks(); + std::copy(contents.begin(), contents.end(), value.begin() + 2); + addChecksum(); + } + + void addChecksum() + { + int sum = std::accumulate(value.begin(), value.end(), 0); + value.back() = static_cast<uint8_t>(256 - sum & 0xff); + } + + uint8_t blocks() const + { + return static_cast<uint8_t>(value.size() / 8); + } + + std::vector<uint8_t> value; +}; + +size_t fillDots(std::vector<uint8_t>::iterator start, size_t count) +{ + *start++ = mklen(count); // prefix with (0xc0 | count) + auto end = start + count++; + std::fill(start, end, '.'); + return count; +} + +size_t fillStr(std::vector<uint8_t>::iterator start, const std::string& str) +{ + size_t count = str.size(); + *start++ = mklen(count++); // prefix with (0xc0 | count) + std::copy(str.begin(), str.end(), start); + return count; +} + +std::vector<uint8_t> genChassisContents() +{ + constexpr size_t pnSize = 18; + constexpr size_t snSize = 18; + constexpr size_t amSize = 31; + constexpr size_t headerSize = 1; + constexpr size_t contentSize = headerSize + 1 + pnSize + 1 + snSize + 1 + + amSize + 1 + amSize + sizeof(eof); + std::vector<uint8_t> data(contentSize); + size_t offset = 0; + // chassis type (main server chassis) + data[offset++] = 0x17; + // chassis part number + offset += fillDots(data.begin() + offset, pnSize); + // chassis serial number + offset += fillDots(data.begin() + offset, snSize); + // info am1 + offset += fillDots(data.begin() + offset, amSize); + // info am2 + offset += fillDots(data.begin() + offset, amSize); + data[offset] = eof; + + return data; +} + +std::vector<uint8_t> genBoardContents(const std::string& name) +{ + constexpr size_t headerSize = 4; + constexpr size_t snSize = 12; + constexpr size_t pnSize = 10; + const std::string version = "FRU Ver 0.01"; + size_t contentSize = headerSize + 1 + name.size() + 1 + intel.size() + 1 + + snSize + 1 + pnSize + 1 + version.size() + sizeof(eof); + std::vector<uint8_t> data(contentSize); + size_t offset = 0; + // chassis type (main server chassis) + data[offset++] = 0; // language code + data[offset++] = 0; // mfg date/time + data[offset++] = 0; // mfg date/time + data[offset++] = 0; // mfg date/time + // manufacturer name + offset += fillStr(data.begin() + offset, intel); + // product name + offset += fillStr(data.begin() + offset, name); + // board sn + offset += fillDots(data.begin() + offset, snSize); + // board pn + offset += fillDots(data.begin() + offset, pnSize); + // fru version string + offset += fillStr(data.begin() + offset, version); + data[offset] = eof; + + return data; +} + +std::vector<uint8_t> genProductContents(const std::string& name) +{ + constexpr size_t headerSize = 1; + constexpr size_t pnSize = 10; + constexpr size_t pvSize = 20; + constexpr size_t snSize = 12; + constexpr size_t atSize = 20; + constexpr size_t idSize = 0; + const std::string version = "FRU Ver 0.01"; + size_t contentSize = headerSize + 1 + intel.size() + 1 + name.size() + 1 + + pnSize + 1 + pvSize + 1 + snSize + 1 + atSize + 1 + + idSize + sizeof(eof); + std::vector<uint8_t> data(contentSize); + size_t offset = 0; + // chassis type (main server chassis) + data[offset++] = 0; // language code + // manufacturer name + offset += fillStr(data.begin() + offset, intel); + // product name + offset += fillStr(data.begin() + offset, name); + // product part number + offset += fillDots(data.begin() + offset, pnSize); + // product version + offset += fillDots(data.begin() + offset, pvSize); + // product serial number + offset += fillDots(data.begin() + offset, snSize); + // product asset tag + offset += fillDots(data.begin() + offset, atSize); + // empty fru file id + offset += fillDots(data.begin() + offset, idSize); + data[offset] = eof; + + return data; +} + +int createFru(const std::string& name) +{ + std::vector<uint8_t> internal{1, 0, 0, 0, 0, 0, 0, 1}; // fixed data + FruEntry chassis(genChassisContents()); + FruEntry board(genBoardContents(name)); + FruEntry product(genProductContents(name)); + uint8_t offset = 1; // room for header's offset + FruEntry header({ + offset += 1, // internal size + offset += chassis.blocks(), + offset += board.blocks(), + }); + std::string filename = name + ".fru.bin"; + std::ofstream output(filename); + std::ostream_iterator<uint8_t> outputIter(output); + std::copy(header.value.begin(), header.value.end(), outputIter); + std::copy(internal.begin(), internal.end(), outputIter); + std::copy(chassis.value.begin(), chassis.value.end(), outputIter); + std::copy(board.value.begin(), board.value.end(), outputIter); + std::copy(product.value.begin(), product.value.end(), outputIter); + constexpr size_t minFruSize = 0x1ff; + size_t fruSize = header.value.size() + internal.size() + + chassis.value.size() + board.value.size() + + product.value.size(); + if (fruSize < minFruSize) + { + std::vector<uint8_t> padding(minFruSize - fruSize); + std::copy(padding.begin(), padding.end(), outputIter); + } + output.close(); + return 0; +} + +int main(int argc, const char* argv[]) +{ + if (argc != 2) + { + std::cerr << "Usage: " << argv[0] << " <'Product Name'>\n"; + return 1; + } + return createFru(argv[1]); +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/host/phosphor-host-postd_git.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/host/phosphor-host-postd_git.bbappend new file mode 100644 index 000000000..0c97b0b86 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/host/phosphor-host-postd_git.bbappend @@ -0,0 +1,4 @@ +DEPENDS += " gtest" + +#SRC_URI = "git://github.com/openbmc/phosphor-host-postd.git" +SRCREV = "996facb65d554f7c0f14c10e3c9252d67a01e21a" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch new file mode 100644 index 000000000..6621d2512 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch @@ -0,0 +1,832 @@ +From 7c005c318a12c53ed887b3081bd4b34ea0213053 Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@intel.com> +Date: Mon, 28 Jun 2021 21:56:18 +0530 +Subject: [PATCH] Firmware update configuration changes + +This commit will provide user to PATCH the below firmware update +attributes before uploding the firmware image. + +1. This will have PATCH support for 'HttpPushUriTargets' and +'HttpPushUriTargetsBusy' attributes. These attributes enables +'HttpPushUri' to distinguish between the firmware update targets. + +2. ApplyOptions are used to specify firmware update specific options +such as ClearConfig which is used while activating the updated +firmware. This setting is maintained in a local static variable +when set using PATCH method. Its used in activate image as input +parameter. This attribute is added as Oem as the default +UpdateService interface doesn't specify any relevant or appropriate +attribute for this. + +Tested: + - GET on "/redfish/v1/UpdateService", got below response +......... + "HttpPushUriTargets": [], + "HttpPushUriTargetsBusy": false +........ + + - PATCH on "/redfish/v1/UpdateService" and works fine. +{ + "HttpPushUriTargets": ["bmc_recovery"], + "HttpPushUriTargetsBusy": true +} + + - Did Firmware update and verified end to end functionality + for both bmc active and backup images. + + - Tested setting ClearConfig to true or false using PATCH + method. + + - Successfully ran redfish validater with no new errors. + +Change-Id: I25ef6d64af3f1dcea3acba93d7fd2b505130142e +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +--- + redfish-core/lib/update_service.hpp | 462 ++++++++++++++---- + static/redfish/v1/$metadata/index.xml | 3 + + .../JsonSchemas/OemUpdateService/index.json | 69 +++ + .../redfish/v1/schema/OemUpdateService_v1.xml | 40 ++ + 4 files changed, 484 insertions(+), 90 deletions(-) + create mode 100644 static/redfish/v1/JsonSchemas/OemUpdateService/index.json + create mode 100644 static/redfish/v1/schema/OemUpdateService_v1.xml + +diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp +index e420130..b3270f0 100644 +--- a/redfish-core/lib/update_service.hpp ++++ b/redfish-core/lib/update_service.hpp +@@ -26,7 +26,9 @@ + + namespace redfish + { +- ++// params for multiple firmware targets ++std::vector<std::string> httpPushUriTargets; ++bool httpPushUriTargetBusy = false; + // Match signals added on software path + static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher; + static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher; +@@ -34,6 +36,17 @@ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher; + static bool fwUpdateInProgress = false; + // Timer for software available + static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer; ++static constexpr const char* versionIntf = ++ "xyz.openbmc_project.Software.Version"; ++static constexpr const char* activationIntf = ++ "xyz.openbmc_project.Software.Activation"; ++static constexpr const char* reqActivationPropName = "RequestedActivation"; ++static constexpr const char* reqActivationsActive = ++ "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"; ++static constexpr const char* reqActivationsStandBySpare = ++ "xyz.openbmc_project.Software.Activation.RequestedActivations.StandbySpare"; ++static constexpr const char* activationsStandBySpare = ++ "xyz.openbmc_project.Software.Activation.Activations.StandbySpare"; + + inline static void cleanUp() + { +@@ -42,28 +55,120 @@ inline static void cleanUp() + fwUpdateErrorMatcher = nullptr; + } + inline static void activateImage(const std::string& objPath, +- const std::string& service) ++ const std::string& service, ++ const std::vector<std::string>& imgUriTargets) + { + BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service; ++ // If targets is empty, it will apply to the active. ++ if (imgUriTargets.size() == 0) ++ { ++ crow::connections::systemBus->async_method_call( ++ [](const boost::system::error_code error_code) { ++ if (error_code) ++ { ++ BMCWEB_LOG_DEBUG ++ << "RequestedActivation failed: error_code = " ++ << error_code; ++ BMCWEB_LOG_DEBUG << "error msg = " << error_code.message(); ++ } ++ }, ++ service, objPath, "org.freedesktop.DBus.Properties", "Set", ++ activationIntf, reqActivationPropName, ++ std::variant<std::string>(reqActivationsActive)); ++ return; ++ } ++ ++ // TODO: Now we support only one target becuase software-manager ++ // code support one activation per object. It will be enhanced ++ // to multiple targets for single image in future. For now, ++ // consider first target alone. + crow::connections::systemBus->async_method_call( +- [](const boost::system::error_code errorCode) { +- if (errorCode) ++ [objPath, service, imgTarget{imgUriTargets[0]}]( ++ const boost::system::error_code ec, ++ const crow::openbmc_mapper::GetSubTreeType& subtree) { ++ if (ec || !subtree.size()) ++ { ++ return; ++ } ++ ++ for (const auto& [invObjPath, invDict] : subtree) + { +- BMCWEB_LOG_DEBUG << "error_code = " << errorCode; +- BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message(); ++ std::size_t idPos = invObjPath.rfind("/"); ++ if ((idPos == std::string::npos) || ++ ((idPos + 1) >= invObjPath.size())) ++ { ++ BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!"; ++ return; ++ } ++ std::string swId = invObjPath.substr(idPos + 1); ++ ++ if (swId != imgTarget) ++ { ++ continue; ++ } ++ ++ if (invDict.size() < 1) ++ { ++ continue; ++ } ++ BMCWEB_LOG_DEBUG << "Image target matched with object " ++ << invObjPath; ++ crow::connections::systemBus->async_method_call( ++ [objPath, ++ service](const boost::system::error_code error_code, ++ const std::variant<std::string> value) { ++ if (error_code) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Error in querying activation value"; ++ // not all fwtypes are updateable, ++ // this is ok ++ return; ++ } ++ std::string activationValue = ++ std::get<std::string>(value); ++ BMCWEB_LOG_DEBUG << "Activation Value: " ++ << activationValue; ++ std::string reqActivation = reqActivationsActive; ++ if (activationValue == activationsStandBySpare) ++ { ++ reqActivation = reqActivationsStandBySpare; ++ } ++ BMCWEB_LOG_DEBUG ++ << "Setting RequestedActivation value as " ++ << reqActivation << " for " << service << " " ++ << objPath; ++ crow::connections::systemBus->async_method_call( ++ [](const boost::system::error_code error_code) { ++ if (error_code) ++ { ++ BMCWEB_LOG_DEBUG ++ << "RequestedActivation failed: ec = " ++ << error_code; ++ } ++ return; ++ }, ++ service, objPath, "org.freedesktop.DBus.Properties", ++ "Set", activationIntf, reqActivationPropName, ++ std::variant<std::string>(reqActivation)); ++ }, ++ invDict[0].first, ++ "/xyz/openbmc_project/software/" + imgTarget, ++ "org.freedesktop.DBus.Properties", "Get", activationIntf, ++ "Activation"); + } + }, +- service, objPath, "org.freedesktop.DBus.Properties", "Set", +- "xyz.openbmc_project.Software.Activation", "RequestedActivation", +- std::variant<std::string>( +- "xyz.openbmc_project.Software.Activation.RequestedActivations." +- "Active")); ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", ++ static_cast<int32_t>(0), std::array<const char*, 1>{versionIntf}); + } + + // Note that asyncResp can be either a valid pointer or nullptr. If nullptr + // then no asyncResp updates will occur + static void + softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ const std::vector<std::string> imgUriTargets, + sdbusplus::message::message& m, + task::Payload&& payload) + { +@@ -76,23 +181,25 @@ static void + + m.read(objPath, interfacesProperties); + +- BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; ++ BMCWEB_LOG_DEBUG << "Software Interface Added. obj path = " << objPath.str; + for (auto& interface : interfacesProperties) + { + BMCWEB_LOG_DEBUG << "interface = " << interface.first; + +- if (interface.first == "xyz.openbmc_project.Software.Activation") ++ if (interface.first == activationIntf) + { + // Retrieve service and activate + crow::connections::systemBus->async_method_call( +- [objPath, asyncResp, payload(std::move(payload))]( ++ [objPath, asyncResp, imgTargets{imgUriTargets}, ++ payload(std::move(payload))]( + const boost::system::error_code errorCode, +- const std::vector< +- std::pair<std::string, std::vector<std::string>>>& +- objInfo) mutable { ++ const std::vector<std::pair< ++ std::string, std::vector<std::string>>>& objInfo) { + if (errorCode) + { +- BMCWEB_LOG_DEBUG << "error_code = " << errorCode; ++ BMCWEB_LOG_DEBUG ++ << "GetSoftwareObject path failed: error_code = " ++ << errorCode; + BMCWEB_LOG_DEBUG << "error msg = " + << errorCode.message(); + if (asyncResp) +@@ -119,7 +226,7 @@ static void + // is added + fwAvailableTimer = nullptr; + +- activateImage(objPath.str, objInfo[0].first); ++ activateImage(objPath.str, objInfo[0].first, imgTargets); + if (asyncResp) + { + std::shared_ptr<task::TaskData> task = +@@ -251,8 +358,7 @@ static void + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str, +- std::array<const char*, 1>{ +- "xyz.openbmc_project.Software.Activation"}); ++ std::array<const char*, 1>{activationIntf}); + } + } + } +@@ -262,7 +368,7 @@ static void + static void monitorForSoftwareAvailable( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const crow::Request& req, const std::string& url, +- int timeoutTimeSeconds = 10) ++ const std::vector<std::string>& imgUriTargets, int timeoutTimeSeconds = 10) + { + // Only allow one FW update at a time + if (fwUpdateInProgress != false) +@@ -301,11 +407,12 @@ static void monitorForSoftwareAvailable( + redfish::messages::internalError(asyncResp->res); + } + }); ++ + task::Payload payload(req); +- auto callback = [asyncResp, ++ auto callback = [asyncResp, imgTargets{imgUriTargets}, + payload](sdbusplus::message::message& m) mutable { + BMCWEB_LOG_DEBUG << "Match fired"; +- softwareInterfaceAdded(asyncResp, m, std::move(payload)); ++ softwareInterfaceAdded(asyncResp, imgTargets, m, std::move(payload)); + }; + + fwUpdateInProgress = true; +@@ -470,12 +577,15 @@ inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app) + std::string fwFile = imageURI.substr(separator + 1); + BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile; + ++ // We will pass empty targets and its handled in activation. ++ std::vector<std::string> httpUriTargets; ++ + // Setup callback for when new software detected + // Give TFTP 10 minutes to complete + monitorForSoftwareAvailable( + asyncResp, req, + "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate", +- 600); ++ httpUriTargets, 600); + + // TFTP can take up to 10 minutes depending on image size and + // connection speed. Return to caller as soon as the TFTP operation +@@ -524,6 +634,9 @@ inline void requestRoutesUpdateService(App& app) + asyncResp->res.jsonValue["Name"] = "Update Service"; + asyncResp->res.jsonValue["HttpPushUri"] = + "/redfish/v1/UpdateService"; ++ asyncResp->res.jsonValue["HttpPushUriTargets"] = httpPushUriTargets; ++ asyncResp->res.jsonValue["HttpPushUriTargetsBusy"] = ++ httpPushUriTargetBusy; + // UpdateService cannot be disabled + asyncResp->res.jsonValue["ServiceEnabled"] = true; + asyncResp->res.jsonValue["FirmwareInventory"] = { +@@ -538,7 +651,8 @@ inline void requestRoutesUpdateService(App& app) + asyncResp->res + .jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; + updateSvcSimpleUpdate["target"] = +- "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; ++ "/redfish/v1/UpdateService/Actions/" ++ "UpdateService.SimpleUpdate"; + updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = + {"TFTP"}; + #endif +@@ -580,89 +694,258 @@ inline void requestRoutesUpdateService(App& app) + "/xyz/openbmc_project/software/apply_time", + "org.freedesktop.DBus.Properties", "Get", + "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime"); ++ ++ // Get the ApplyOptions value ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec, ++ const std::variant<bool> applyOption) { ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG << "DBUS response error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ const bool* b = std::get_if<bool>(&applyOption); ++ ++ if (b) ++ { ++ asyncResp->res ++ .jsonValue["Oem"]["ApplyOptions"]["@odata.type"] = ++ "#OemUpdateService.ApplyOptions"; ++ asyncResp->res ++ .jsonValue["Oem"]["ApplyOptions"]["ClearConfig"] = ++ *b; ++ } ++ }, ++ "xyz.openbmc_project.Software.BMC.Updater", ++ "/xyz/openbmc_project/software", ++ "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.Software.ApplyOptions", "ClearConfig"); + }); ++ + BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/") + .privileges(redfish::privileges::patchUpdateService) +- .methods(boost::beast::http::verb::patch)( +- [](const crow::Request& req, +- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { +- BMCWEB_LOG_DEBUG << "doPatch..."; ++ .methods( ++ boost::beast::http::verb:: ++ patch)([](const crow::Request& req, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ BMCWEB_LOG_DEBUG << "doPatch..."; ++ ++ std::optional<nlohmann::json> pushUriOptions; ++ std::optional<std::vector<std::string>> imgTargets; ++ std::optional<bool> imgTargetBusy; ++ std::optional<nlohmann::json> oemProps; ++ if (!json_util::readJson(req, asyncResp->res, "HttpPushUriOptions", ++ pushUriOptions, "HttpPushUriTargets", ++ imgTargets, "HttpPushUriTargetsBusy", ++ imgTargetBusy, "Oem", oemProps)) ++ { ++ BMCWEB_LOG_DEBUG ++ << "UpdateService doPatch: Invalid request body"; ++ return; ++ } ++ ++ if (oemProps) ++ { ++ std::optional<nlohmann::json> applyOptions; ++ ++ if (!json_util::readJson(*oemProps, asyncResp->res, ++ "ApplyOptions", applyOptions)) ++ { ++ return; ++ } ++ ++ if (applyOptions) ++ { ++ std::optional<bool> clearConfig; ++ if (!json_util::readJson(*applyOptions, asyncResp->res, ++ "ClearConfig", clearConfig)) ++ { ++ return; ++ } + +- std::optional<nlohmann::json> pushUriOptions; +- if (!json_util::readJson(req, asyncResp->res, +- "HttpPushUriOptions", pushUriOptions)) ++ if (clearConfig) ++ { ++ // Set the requested image apply time value ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "D-Bus responses error: " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ messages::success(asyncResp->res); ++ }, ++ "xyz.openbmc_project.Software.BMC.Updater", ++ "/xyz/openbmc_project/software", ++ "org.freedesktop.DBus.Properties", "Set", ++ "xyz.openbmc_project.Software.ApplyOptions", ++ "ClearConfig", std::variant<bool>{*clearConfig}); ++ } ++ } ++ } ++ if (pushUriOptions) ++ { ++ std::optional<nlohmann::json> pushUriApplyTime; ++ if (!json_util::readJson(*pushUriOptions, asyncResp->res, ++ "HttpPushUriApplyTime", ++ pushUriApplyTime)) + { + return; + } + +- if (pushUriOptions) ++ if (pushUriApplyTime) + { +- std::optional<nlohmann::json> pushUriApplyTime; +- if (!json_util::readJson(*pushUriOptions, asyncResp->res, +- "HttpPushUriApplyTime", +- pushUriApplyTime)) ++ std::optional<std::string> applyTime; ++ if (!json_util::readJson(*pushUriApplyTime, asyncResp->res, ++ "ApplyTime", applyTime)) + { + return; + } + +- if (pushUriApplyTime) ++ if (applyTime) + { +- std::optional<std::string> applyTime; +- if (!json_util::readJson(*pushUriApplyTime, +- asyncResp->res, "ApplyTime", +- applyTime)) ++ std::string applyTimeNewVal; ++ if (applyTime == "Immediate") + { ++ applyTimeNewVal = ++ "xyz.openbmc_project.Software.ApplyTime." ++ "RequestedApplyTimes.Immediate"; ++ } ++ else if (applyTime == "OnReset") ++ { ++ applyTimeNewVal = ++ "xyz.openbmc_project.Software.ApplyTime." ++ "RequestedApplyTimes.OnReset"; ++ } ++ else ++ { ++ BMCWEB_LOG_INFO ++ << "ApplyTime value is not in the list of " ++ "acceptable values"; ++ messages::propertyValueNotInList( ++ asyncResp->res, *applyTime, "ApplyTime"); + return; + } + +- if (applyTime) ++ // Set the requested image apply time value ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "D-Bus responses error: " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ messages::success(asyncResp->res); ++ }, ++ "xyz.openbmc_project.Settings", ++ "/xyz/openbmc_project/software/apply_time", ++ "org.freedesktop.DBus.Properties", "Set", ++ "xyz.openbmc_project.Software.ApplyTime", ++ "RequestedApplyTime", ++ std::variant<std::string>{applyTimeNewVal}); ++ } ++ } ++ } ++ if (imgTargetBusy) ++ { ++ if ((httpPushUriTargetBusy) && (*imgTargetBusy)) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Other client has reserved the HttpPushUriTargets " ++ "property for firmware updates."; ++ messages::resourceInUse(asyncResp->res); ++ return; ++ } ++ ++ if (imgTargets) ++ { ++ if (!(*imgTargetBusy)) ++ { ++ BMCWEB_LOG_DEBUG ++ << "UpdateService doPatch: httpPushUriTargetBusy " ++ "should be " ++ "true before setting httpPushUriTargets"; ++ messages::invalidObject(asyncResp->res, ++ "HttpPushUriTargetsBusy"); ++ return; ++ } ++ if ((*imgTargets).size() != 0) ++ { ++ // TODO: Now we support max one target becuase ++ // software-manager code support one activation per ++ // object. It will be enhanced to multiple targets for ++ // single image in future. For now, consider first ++ // target alone. ++ if ((*imgTargets).size() != 1) + { +- std::string applyTimeNewVal; +- if (applyTime == "Immediate") +- { +- applyTimeNewVal = +- "xyz.openbmc_project.Software.ApplyTime." +- "RequestedApplyTimes.Immediate"; +- } +- else if (applyTime == "OnReset") +- { +- applyTimeNewVal = +- "xyz.openbmc_project.Software.ApplyTime." +- "RequestedApplyTimes.OnReset"; +- } +- else +- { +- BMCWEB_LOG_INFO +- << "ApplyTime value is not in the list of " +- "acceptable values"; +- messages::propertyValueNotInList( +- asyncResp->res, *applyTime, "ApplyTime"); +- return; +- } ++ messages::invalidObject(asyncResp->res, ++ "HttpPushUriTargets"); ++ return; ++ } ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, uriTargets{*imgTargets}, ++ targetBusy{*imgTargetBusy}]( ++ const boost::system::error_code ec, ++ const std::vector<std::string> swInvPaths) { ++ if (ec) ++ { ++ return; ++ } + +- // Set the requested image apply time value +- crow::connections::systemBus->async_method_call( +- [asyncResp]( +- const boost::system::error_code ec) { +- if (ec) ++ bool swInvObjFound = false; ++ for (const std::string& path : swInvPaths) ++ { ++ std::size_t idPos = path.rfind("/"); ++ if ((idPos == std::string::npos) || ++ ((idPos + 1) >= path.size())) + { +- BMCWEB_LOG_ERROR +- << "D-Bus responses error: " << ec; + messages::internalError(asyncResp->res); ++ BMCWEB_LOG_DEBUG ++ << "Can't parse firmware ID!!"; + return; + } +- messages::success(asyncResp->res); +- }, +- "xyz.openbmc_project.Settings", +- "/xyz/openbmc_project/software/apply_time", +- "org.freedesktop.DBus.Properties", "Set", +- "xyz.openbmc_project.Software.ApplyTime", +- "RequestedApplyTime", +- std::variant<std::string>{applyTimeNewVal}); +- } ++ std::string swId = path.substr(idPos + 1); ++ ++ if (swId == uriTargets[0]) ++ { ++ swInvObjFound = true; ++ break; ++ } ++ } ++ if (!swInvObjFound) ++ { ++ messages::invalidObject( ++ asyncResp->res, "HttpPushUriTargets"); ++ return; ++ } ++ httpPushUriTargetBusy = targetBusy; ++ httpPushUriTargets = uriTargets; ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", ++ "GetSubTreePaths", "/", static_cast<int32_t>(0), ++ std::array<const char*, 1>{versionIntf}); ++ } ++ else ++ { ++ httpPushUriTargetBusy = *imgTargetBusy; ++ httpPushUriTargets = *imgTargets; + } + } +- }); ++ else ++ { ++ httpPushUriTargetBusy = *imgTargetBusy; ++ } ++ } ++ }); ++ + BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/") + .privileges(redfish::privileges::postUpdateService) + .methods(boost::beast::http::verb::post)( +@@ -672,7 +955,8 @@ inline void requestRoutesUpdateService(App& app) + + // Setup callback for when new software detected + monitorForSoftwareAvailable(asyncResp, req, +- "/redfish/v1/UpdateService"); ++ "/redfish/v1/UpdateService", ++ httpPushUriTargets); + + std::string filepath("/tmp/images/" + + boost::uuids::to_string( +@@ -685,7 +969,7 @@ inline void requestRoutesUpdateService(App& app) + out.close(); + BMCWEB_LOG_DEBUG << "file upload complete!!"; + }); +-} ++} // namespace redfish + + inline void requestRoutesSoftwareInventoryCollection(App& app) + { +@@ -748,8 +1032,7 @@ inline void requestRoutesSoftwareInventoryCollection(App& app) + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/software", static_cast<int32_t>(0), +- std::array<const char*, 1>{ +- "xyz.openbmc_project.Software.Version"}); ++ std::array<const char*, 1>{versionIntf}); + }); + } + /* Fill related item links (i.e. bmc, bios) in for inventory */ +@@ -913,7 +1196,7 @@ inline void requestRoutesSoftwareInventory(App& app) + }, + obj.second[0].first, obj.first, + "org.freedesktop.DBus.Properties", "GetAll", +- "xyz.openbmc_project.Software.Version"); ++ versionIntf); + } + if (!found) + { +@@ -937,8 +1220,7 @@ inline void requestRoutesSoftwareInventory(App& app) + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", + static_cast<int32_t>(0), +- std::array<const char*, 1>{ +- "xyz.openbmc_project.Software.Version"}); ++ std::array<const char*, 1>{versionIntf}); + }); + } + +diff --git a/static/redfish/v1/$metadata/index.xml b/static/redfish/v1/$metadata/index.xml +index 66b6faf..f0919c9 100644 +--- a/static/redfish/v1/$metadata/index.xml ++++ b/static/redfish/v1/$metadata/index.xml +@@ -2444,6 +2444,9 @@ + <edmx:Reference Uri="/redfish/v1/schema/OemComputerSystem_v1.xml"> + <edmx:Include Namespace="OemComputerSystem"/> + </edmx:Reference> ++ <edmx:Reference Uri="/redfish/v1/schema/OemUpdateService_v1.xml"> ++ <edmx:Include Namespace="OemUpdateService"/> ++ </edmx:Reference> + <edmx:Reference Uri="/redfish/v1/schema/OemVirtualMedia_v1.xml"> + <edmx:Include Namespace="OemVirtualMedia"/> + <edmx:Include Namespace="OemVirtualMedia.v1_0_0"/> +diff --git a/static/redfish/v1/JsonSchemas/OemUpdateService/index.json b/static/redfish/v1/JsonSchemas/OemUpdateService/index.json +new file mode 100644 +index 0000000..74e39cd +--- /dev/null ++++ b/static/redfish/v1/JsonSchemas/OemUpdateService/index.json +@@ -0,0 +1,69 @@ ++{ ++ "$id": "http://redfish.dmtf.org/schemas/v1/OemUpdateService.json", ++ "$schema": "http://redfish.dmtf.org/schemas/v1/redfish-schema-v1.json", ++ "copyright": "Copyright 2014-2019 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright", ++ "definitions": { ++ "ApplyOptions": { ++ "additionalProperties": false, ++ "description": "An indication by boolean value whether to update firmware configuration along with firmware image update.", ++ "patternProperties": { ++ "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": { ++ "description": "This property shall specify a valid odata or Redfish property.", ++ "type": [ ++ "array", ++ "boolean", ++ "integer", ++ "number", ++ "null", ++ "object", ++ "string" ++ ] ++ } ++ }, ++ "properties": { ++ "ClearConfig": { ++ "description": "This indicates whether to update firmware configuration or not.", ++ "longDescription": "The value of this property is used to indicate the firmware configuration update.", ++ "readonly": false, ++ "type": [ ++ "boolean", ++ "null" ++ ] ++ } ++ }, ++ "type": "object" ++ }, ++ "Oem": { ++ "additionalProperties": true, ++ "description": "OemUpdateService Oem properties.", ++ "patternProperties": { ++ "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": { ++ "description": "This property shall specify a valid odata or Redfish property.", ++ "type": [ ++ "array", ++ "boolean", ++ "integer", ++ "number", ++ "null", ++ "object", ++ "string" ++ ] ++ } ++ }, ++ "properties": { ++ "ApplyOptions": { ++ "anyOf": [ ++ { ++ "$ref": "#/definitions/ApplyOptions" ++ }, ++ { ++ "type": "null" ++ } ++ ] ++ } ++ }, ++ "type": "object" ++ } ++ }, ++ "title": "#OemUpdateService" ++} +diff --git a/static/redfish/v1/schema/OemUpdateService_v1.xml b/static/redfish/v1/schema/OemUpdateService_v1.xml +new file mode 100644 +index 0000000..cbb7aa4 +--- /dev/null ++++ b/static/redfish/v1/schema/OemUpdateService_v1.xml +@@ -0,0 +1,40 @@ ++<?xml version="1.0" encoding="UTF-8"?> ++<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0"> ++ <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml"> ++ <edmx:Include Namespace="Org.OData.Core.V1" Alias="OData" /> ++ </edmx:Reference> ++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/RedfishExtensions_v1.xml"> ++ <edmx:Include Namespace="Validation.v1_0_0" Alias="Validation"/> ++ <edmx:Include Namespace="RedfishExtensions.v1_0_0" Alias="Redfish"/> ++ </edmx:Reference> ++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/UpdateService_v1.xml"> ++ <edmx:Include Namespace="UpdateService"/> ++ <edmx:Include Namespace="UpdateService.v1_4_0"/> ++ </edmx:Reference> ++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Resource_v1.xml"> ++ <edmx:Include Namespace="Resource"/> ++ <edmx:Include Namespace="Resource.v1_0_0"/> ++ </edmx:Reference> ++ ++ <edmx:DataServices> ++ <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OemUpdateService"> ++ <ComplexType Name="Oem" BaseType="Resource.OemObject"> ++ <Annotation Term="OData.AdditionalProperties" Bool="true" /> ++ <Annotation Term="OData.Description" String="OemUpdateService Oem properties." /> ++ <Annotation Term="OData.AutoExpand"/> ++ <Property Name="ApplyOptions" Type="OemUpdateService.ApplyOptions"/> ++ </ComplexType> ++ ++ <ComplexType Name="ApplyOptions" BaseType="Resource.OemObject"> ++ <Annotation Term="OData.AdditionalProperties" Bool="false" /> ++ <Annotation Term="OData.Description" String="An indication by boolean value whether to update firmware configuration along with firmware image update." /> ++ <Property Name="ClearConfig" Type="Edm.Boolean"> ++ <Annotation Term="OData.Permissions" EnumMember="OData.Permission/ReadWrite"/> ++ <Annotation Term="OData.Description" String="This indicates whether to update firmware configuration or not."/> ++ <Annotation Term="OData.LongDescription" String="The value of this property is used to indicate the firmware configuration update."/> ++ </Property> ++ </ComplexType> ++ ++ </Schema> ++ </edmx:DataServices> ++</edmx:Edmx> +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch new file mode 100644 index 000000000..02f843bb8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch @@ -0,0 +1,74 @@ +From 034920eca21bc25899565484928ee72025e21ff8 Mon Sep 17 00:00:00 2001 +From: Wiktor Golgowski <wiktor.golgowski@linux.intel.com> +Date: Thu, 30 Apr 2020 11:09:35 +0200 +Subject: [PATCH] Use chip id-based UUID for Service Root. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If the sysfs-provided chip id is available, it will be used as +payload to generate Service Root UUID from hardcoded namespace. + +Tested: +Generated UUID is consistent between BMC image reflashes. +If the sysfs node is not available, code falls back to randomly +generated UUID. + +Signed-off-by: Wiktor Gołgowski <wiktor.golgowski@linux.intel.com> +--- + include/persistent_data.hpp | 32 +++++++++++++++++++++++++++++--- + 1 file changed, 29 insertions(+), 3 deletions(-) + +diff --git a/include/persistent_data.hpp b/include/persistent_data.hpp +index 24f7afd..8826b06 100644 +--- a/include/persistent_data.hpp ++++ b/include/persistent_data.hpp +@@ -25,6 +25,10 @@ class ConfigFile + public: + // todo(ed) should read this from a fixed location somewhere, not CWD + static constexpr const char* filename = "bmcweb_persistent_data.json"; ++ static constexpr const char* chipIdSysfsNode = "/sys/devices/platform" ++ "/ahb/ahb:apb/1e6e2000.syscon/1e6e2000.syscon:misc_control/chip_id"; ++ static constexpr const char* UuidNs = "{b7b0553a-54cc-4162-982d-" ++ "944847ed76f5}"; + + ConfigFile() + { +@@ -144,9 +148,31 @@ class ConfigFile + + if (systemUuid.empty()) + { +- systemUuid = +- boost::uuids::to_string(boost::uuids::random_generator()()); +- needWrite = true; ++ // Try to retrieve chip id-based uuid. ++ std::ifstream chipIdFile(chipIdSysfsNode); ++ if (chipIdFile.is_open()) ++ { ++ std::string chipId; ++ std::getline(chipIdFile, chipId); ++ if (!chipId.empty()) ++ { ++ boost::uuids::name_generator_sha1 gen( ++ boost::uuids::string_generator()(UuidNs)); ++ systemUuid = boost::uuids::to_string(gen(chipId.c_str())); ++ needWrite = true; ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "Cannot get chip id-based System UUID."; ++ } ++ } ++ // If the above fails, generate random uuid. ++ if (systemUuid.empty()) ++ { ++ systemUuid = ++ boost::uuids::to_string(boost::uuids::random_generator()()); ++ needWrite = true; ++ } + } + if (fileRevision < jsonRevision) + { +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch new file mode 100644 index 000000000..e54e495bb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch @@ -0,0 +1,60 @@ +From 971aa5058ac4bb626eeadf8b00738737748ed549 Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Tue, 29 Jun 2021 15:25:38 +0000 +Subject: [PATCH] managers: add attributes for Manager.CommandShell + +Issue: ConnectTypesSupported, ServiceEnabled and + MaxConcurrentSessions Attributes are missing for + Manager.CommandShell, though Requirement mandates it. + +Fix: Added missing attributes to Manager.CommandShell + +Tested: +1. Verified redfish validator passed +2. Get bmc details from Redfish +Redfish URI: https://<BMC IP>/redfish/v1/Managers/bmc +Response: +{ + "@odata.id": "/redfish/v1/Managers/bmc", + "@odata.type": "#Manager.v1_9_0.Manager", +.... +.... + "CommandShell": { + "ConnectTypesSupported": [ + "SSH", + "IPMI" + ], + "MaxConcurrentSessions": 4, + "ServiceEnabled": true + }, +.... +.... + +Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +Change-Id: I2a56db912fc81064098f7aa9f4d110ac3baf361d +--- + redfish-core/lib/managers.hpp | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp +index b286f19..186003b 100644 +--- a/redfish-core/lib/managers.hpp ++++ b/redfish-core/lib/managers.hpp +@@ -1998,6 +1998,14 @@ inline void requestRoutesManager(App& app) + 15; + asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = + {"IPMI", "SSH"}; ++ ++ // Fill in CommandShell info ++ asyncResp->res.jsonValue["CommandShell"]["ServiceEnabled"] = true; ++ asyncResp->res.jsonValue["CommandShell"]["MaxConcurrentSessions"] = ++ 4; ++ asyncResp->res.jsonValue["CommandShell"]["ConnectTypesSupported"] = ++ {"SSH", "IPMI"}; ++ + #ifdef BMCWEB_ENABLE_KVM + // Fill in GraphicalConsole info + asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch new file mode 100644 index 000000000..f41e6f994 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch @@ -0,0 +1,158 @@ +From b9747ecfce682f15dce0bb6e41e0c894f29419f3 Mon Sep 17 00:00:00 2001 +From: Snehalatha Venkatesh <snehalathax.v@intel.com> +Date: Thu, 8 Apr 2021 14:42:07 +0000 +Subject: [PATCH] bmcweb: Add PhysicalContext to Thermal resources + +Adding PhysicalContext to make redfish data compliance with OCP +Server Mgmt Interface v0.2.1.pdf and specific to Thermal resources. +https://github.com/opencomputeproject/OCP-Profiles/blob/master/ +OCPServerHardwareManagement.v0_2_4.json + +Tested: +1. Redfish validator - passed for this new change +2. GET - https://<bmc.ip>/redfish/v1/Chassis/<Board>/Thermal +Response: +{ + "@odata.id": "/redfish/v1/Chassis/<Board>/Thermal#/Temperatures/0", + "@odata.type": "#Thermal.v1_3_0.Temperature", + "LowerThresholdCritical": 0.0, + "LowerThresholdNonCritical": 5.0, + "MaxReadingRangeTemp": 127.0, + "MemberId": "BMC_Temp", + "MinReadingRangeTemp": -128.0, + "Name": "BMC Temp", + "PhysicalContext": "SystemBoard", + "ReadingCelsius": 25.75, + "Status": { + "Health": "OK", + "State": "Enabled" + }, + "UpperThresholdCritical": 115.0, + "UpperThresholdNonCritical": 110.0 +}, +{ + "@odata.id": "/redfish/v1/Chassis/<Board>/Thermal#/Temperatures/1", + "@odata.type": "#Thermal.v1_3_0.Temperature", + "LowerThresholdCritical": 0.0, + "LowerThresholdNonCritical": 5.0, + "MaxReadingRangeTemp": 255.0, + "MemberId": "CPU1_P12V_PVCCIN_VR_Temp", + "MinReadingRangeTemp": 0.0, + "Name": "CPU1 P12V PVCCIN VR Temp", + "PhysicalContext": "CPU", + "ReadingCelsius": 41.0, + "Status": { + "Health": "OK", + "State": "Enabled" + }, + "UpperThresholdCritical": 115.0, + "UpperThresholdNonCritical": 110.0 +}, +{ + "@odata.id": "/redfish/v1/Chassis/<Board>/Thermal#/Temperatures/28", + "@odata.type": "#Thermal.v1_3_0.Temperature", + "LowerThresholdCritical": 0.0, + "LowerThresholdNonCritical": 5.0, + "MaxReadingRangeTemp": 127.0, + "MemberId": "Inlet_BRD_Temp", + "MinReadingRangeTemp": -128.0, + "Name": "Inlet BRD Temp", + "PhysicalContext": "Intake", + "ReadingCelsius": 23.187, + "Status": { + "Health": "OK", + "State": "Enabled" + }, + "UpperThresholdCritical": 115.0, + "UpperThresholdNonCritical": 110.0 +}, +{ + @odata.id": "/redfish/v1/Chassis/F2U8X25_HSBP_2/Thermal#/Temperatures/0", + @odata.type": "#Thermal.v1_3_0.Temperature", + LowerThresholdCritical": 7.0, + LowerThresholdNonCritical": 12.0, + MaxReadingRangeTemp": 127.0, + MemberId": "HSBP2_Temp", + MinReadingRangeTemp": -128.0, + Name": "HSBP2 Temp", + PhysicalContext": "Backplane", + ReadingCelsius": 21.437, + Status": { + "Health": "OK", + "State": "Enabled" + }, + UpperThresholdCritical": 57.0, + UpperThresholdNonCritical": 52.0 +} +3. GET - https://<bmc.ip>/redfish/v1/Chassis/<Board>/Power +Response: +{ + "@odata.id": "/redfish/v1/Chassis/<Board>/Power#/Voltages/3", + "@odata.type": "#Power.v1_0_0.Voltage", + "LowerThresholdCritical": 1.648, + "LowerThresholdNonCritical": 1.699, + "MaxReadingRange": 2.3984009912875566, + "MemberId": "P1V8_PCH", + "MinReadingRange": 0.0, + "Name": "P1V8 PCH", + "ReadingVolts": 1.8055, + "Status": { + "Health": "OK", + "State": "Enabled" + }, + "UpperThresholdCritical": 1.961, + "UpperThresholdNonCritical": 1.904 +} +4. GET - https://<bmc.ip>/redfish/v1/Chassis/<Board>/Sensors/PSU1_Input_Current +Response: +{ + "@odata.id": "/redfish/v1/Chassis/<Board>/Sensors/PSU1_Input_Current", + "@odata.type": "#Sensor.v1_0_0.Sensor", + "Id": "PSU1_Input_Current", + "Name": "PSU1 Input Current", + "Reading": 0.947, + "ReadingRangeMax": 12.0, + "ReadingRangeMin": 0.0, + "ReadingType": "Current", + "ReadingUnits": "A", + "Status": { + "Health": "OK", + "State": "Enabled" + } +} +Signed-off-by: Snehalatha Venkatesh <snehalathax.v@intel.com> +Signed-off-by: sunitakx <sunitax.kumari@linux.intel.com> +--- + redfish-core/lib/sensors.hpp | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp +index 5d27577..d51d09f 100644 +--- a/redfish-core/lib/sensors.hpp ++++ b/redfish-core/lib/sensors.hpp +@@ -973,6 +973,22 @@ inline void objectInterfacesToJson( + { + unit = "/ReadingCelsius"_json_pointer; + sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature"; ++ if (sensorName.find("CPU") != std::string::npos) ++ { ++ sensorJson["PhysicalContext"] = "CPU"; ++ } ++ else if (sensorName.find("Inlet") != std::string::npos) ++ { ++ sensorJson["PhysicalContext"] = "Intake"; ++ } ++ else if (sensorName.find("HSBP") != std::string::npos) ++ { ++ sensorJson["PhysicalContext"] = "Backplane"; ++ } ++ else ++ { ++ sensorJson["PhysicalContext"] = "SystemBoard"; ++ } + // TODO(ed) Documentation says that path should be type fan_tach, + // implementation seems to implement fan + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch new file mode 100644 index 000000000..3ef4ee2de --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch @@ -0,0 +1,67 @@ +From 1f572a1991fc8d9b08689aa6e3470080467977a7 Mon Sep 17 00:00:00 2001 +From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +Date: Thu, 15 Apr 2021 10:59:42 +0000 +Subject: [PATCH] Log RedFish event for Invalid login attempt + +This commit adds support for logging RedFish event log while user tries +to attempt login with invalid credentials. +When user trying to login with invalid credentials on HTTPS interface +like WebUI and RedFish, event should be logged in RedFish event log. +This event log is useful for further analysis to debug the root-cause +for failure. + +Tested: +1. Verified RedFish validator passed +2. Login with wrong credentials on HTTPS interface. +3. Verified for RedFish/WebUI events. RedFish event logged successfully. +GET: https://BMC-IP/redfish/v1/Systems/system/LogServices/ + EventLog/Entries +Response: +"Members": [ +{ + "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/ + Entries/1618466128", + "@odata.type": "#LogEntry.v1_4_0.LogEntry", + "Created": "2021-04-15T05:55:28+00:00", + "EntryType": "Event", + "Id": "1618466128", + "Message": "Invalid username or password attempted on HTTPS.", + "MessageArgs": [ + "HTTPS" + ], + "MessageId": "OpenBMC.0.1.InvalidLoginAttempted", + "Name": "System Event Log Entry", + "Severity": "Warning" +} + +Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +--- + include/pam_authenticate.hpp | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/include/pam_authenticate.hpp b/include/pam_authenticate.hpp +index 12f19c0..01bf301 100644 +--- a/include/pam_authenticate.hpp ++++ b/include/pam_authenticate.hpp +@@ -1,6 +1,7 @@ + #pragma once + + #include <security/pam_appl.h> ++#include <systemd/sd-journal.h> + + #include <boost/utility/string_view.hpp> + +@@ -75,6 +76,10 @@ inline int pamAuthenticateUser(const std::string_view username, + PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); + if (retval != PAM_SUCCESS) + { ++ sd_journal_send("MESSAGE= %s", "Invalid login attempted on HTTPS", ++ "PRIORITY=%i", LOG_WARNING, "REDFISH_MESSAGE_ID=%s", ++ "OpenBMC.0.1.InvalidLoginAttempted", ++ "REDFISH_MESSAGE_ARGS=%s", "HTTPS", NULL); + pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval + return retval; + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch new file mode 100644 index 000000000..41acb6057 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch @@ -0,0 +1,59 @@ +From 6c10adb53d3247f65e5d9399290e6b8e7962cdef Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 28 Apr 2021 17:19:50 -0700 +Subject: [PATCH] Add UART routing logic into host console connection flow + +Switching UART routing when starting obmc-service introduces garbled +character printing out on physical host serial output and it's +inevitable so this commit moves the routing logic into host console +connection flow in bmcweb to avoid the issue until SOL is actually +activated. + +Tested: The garbled character printing out was not observed during +BMC booting. SOL worked well. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + include/obmc_console.hpp | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/include/obmc_console.hpp b/include/obmc_console.hpp +index cdb19901e82d..9c4ae8821074 100644 +--- a/include/obmc_console.hpp ++++ b/include/obmc_console.hpp +@@ -22,6 +22,9 @@ static boost::container::flat_set<crow::websocket::Connection*> sessions; + + static bool doingWrite = false; + ++constexpr char const* uartMuxCtrlPath = "/sys/bus/platform/drivers/aspeed-uart-routing/1e789098.uart-routing/hicra"; ++constexpr char const* uartMuxCtrlVal = "0x03450003"; ++ + inline void doWrite() + { + if (doingWrite) +@@ -110,6 +113,22 @@ inline void connectHandler(const boost::system::error_code& ec) + return; + } + ++ FILE* file = fopen(uartMuxCtrlPath, "w"); ++ if (file != nullptr) ++ { ++ int rc = fputs(uartMuxCtrlVal, file); ++ fclose(file); ++ if (rc < 0) ++ { ++ BMCWEB_LOG_ERROR << "Couldn't change UART routing: " << rc; ++ for (crow::websocket::Connection* session : sessions) ++ { ++ session->close("Error in connecting to host port"); ++ } ++ return; ++ } ++ } ++ + doWrite(); + doRead(); + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0014-recommended-fixes-by-crypto-review-team.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0014-recommended-fixes-by-crypto-review-team.patch new file mode 100644 index 000000000..5ffc259c0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0014-recommended-fixes-by-crypto-review-team.patch @@ -0,0 +1,75 @@ +From aaaa117817687a05284f8bfff07e2404e0d616b7 Mon Sep 17 00:00:00 2001 +From: Radivoje Jovanovic <radivoje.jovanovic@intel.com> +Date: Thu, 10 Dec 2020 13:42:20 -0800 +Subject: [PATCH] recommended fixes by crypto review team + +some curves/cyphers are forbiden to be used by +Intel crypto team. +Only enable approved ones. +the patch was created by aleksandr.v.tereschenko@intel.com + +Signed-off-by: Radivoje Jovanovic <radivoje.jovanovic@intel.com> +--- + include/ssl_key_handler.hpp | 39 ++++++++++++++++++++----------------- + 1 file changed, 21 insertions(+), 18 deletions(-) + +diff --git a/include/ssl_key_handler.hpp b/include/ssl_key_handler.hpp +index 39e83d7..8de7349 100644 +--- a/include/ssl_key_handler.hpp ++++ b/include/ssl_key_handler.hpp +@@ -381,31 +381,34 @@ inline std::shared_ptr<boost::asio::ssl::context> + mSslContext->use_private_key_file(sslPemFile, + boost::asio::ssl::context::pem); + +- // Set up EC curves to auto (boost asio doesn't have a method for this) +- // There is a pull request to add this. Once this is included in an asio +- // drop, use the right way +- // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue +- if (SSL_CTX_set_ecdh_auto(mSslContext->native_handle(), 1) != 1) ++ std::string handshakeCurves = "P-384:P-521:X448"; ++ if (SSL_CTX_set1_groups_list(mSslContext->native_handle(), handshakeCurves.c_str()) != 1) + { +- BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n"; ++ BMCWEB_LOG_ERROR << "Error setting ECDHE group list\n"; + } + +- std::string mozillaModern = "ECDHE-ECDSA-AES256-GCM-SHA384:" +- "ECDHE-RSA-AES256-GCM-SHA384:" +- "ECDHE-ECDSA-CHACHA20-POLY1305:" +- "ECDHE-RSA-CHACHA20-POLY1305:" +- "ECDHE-ECDSA-AES128-GCM-SHA256:" +- "ECDHE-RSA-AES128-GCM-SHA256:" +- "ECDHE-ECDSA-AES256-SHA384:" +- "ECDHE-RSA-AES256-SHA384:" +- "ECDHE-ECDSA-AES128-SHA256:" +- "ECDHE-RSA-AES128-SHA256"; ++ std::string tls12Ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384:" ++ "ECDHE-RSA-AES256-GCM-SHA384"; ++ std::string tls13Ciphers = "TLS_AES_256_GCM_SHA384"; + + if (SSL_CTX_set_cipher_list(mSslContext->native_handle(), +- mozillaModern.c_str()) != 1) ++ tls12Ciphers.c_str()) != 1) + { +- BMCWEB_LOG_ERROR << "Error setting cipher list\n"; ++ BMCWEB_LOG_ERROR << "Error setting TLS 1.2 cipher list\n"; + } ++ ++ if (SSL_CTX_set_ciphersuites(mSslContext->native_handle(), ++ tls13Ciphers.c_str()) != 1) ++ { ++ BMCWEB_LOG_ERROR << "Error setting TLS 1.3 cipher list\n"; ++ } ++ ++ if ((SSL_CTX_set_options(mSslContext->native_handle(), ++ SSL_OP_CIPHER_SERVER_PREFERENCE) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0) ++ { ++ BMCWEB_LOG_ERROR << "Error setting TLS server preference option\n"; ++ } ++ + return mSslContext; + } + } // namespace ensuressl +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0015-Add-state-sensor-messages-to-the-registry.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0015-Add-state-sensor-messages-to-the-registry.patch new file mode 100644 index 000000000..1193fe9d1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0015-Add-state-sensor-messages-to-the-registry.patch @@ -0,0 +1,98 @@ +From b468b508176dfffe2e8e4adb3052577b9ff70d2f Mon Sep 17 00:00:00 2001 +From: "Arun P. Mohanan" <arun.p.m@linux.intel.com> +Date: Wed, 27 Jan 2021 18:22:58 +0530 +Subject: [PATCH] Add state sensor messages to the registry + +Add messages to registry to indicate state sensor state change. + +Tested: +Build and redfish validator passes. +Logged these events and confirmed that they appear as expected on +Redfish. +GET: https://<BMC IP>/redfish/v1/Systems/system/LogServices/EventLog/Entries/1612528180 +{ + "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/1612528180", + "@odata.type": "#LogEntry.v1_4_0.LogEntry", + "Created": "2021-02-05T12:29:40+00:00", + "EntryType": "Event", + "Id": "1612528180", + "Message": "Operational Fault Status of Card_health_1 state sensor changed from Error to Normal.", + "MessageArgs": [ + "Operational Fault Status", + "Card_health_1", + "Error", + "Normal" + ], + "MessageId": "OpenBMC.0.1.StateSensorNormal", + "Name": "System Event Log Entry", + "Severity": "OK" +} + +Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com> +--- + .../registries/openbmc_message_registry.hpp | 36 +++++++++++++++++-- + 1 file changed, 34 insertions(+), 2 deletions(-) + +diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp +index 6cf503f..87b2913 100644 +--- a/redfish-core/include/registries/openbmc_message_registry.hpp ++++ b/redfish-core/include/registries/openbmc_message_registry.hpp +@@ -29,7 +29,7 @@ const Header header = { + "0.2.0", + "OpenBMC", + }; +-constexpr std::array<MessageEntry, 188> registry = { ++constexpr std::array<MessageEntry, 191> registry = { + MessageEntry{ + "ADDDCCorrectable", + { +@@ -2331,6 +2331,39 @@ constexpr std::array<MessageEntry, 188> registry = { + {}, + "None.", + }}, ++ MessageEntry{ ++ "StateSensorNormal", ++ { ++ "Indicates that a state sensor has changed state to normal.", ++ "%1 of %2 state sensor changed from %3 to %4.", ++ "OK", ++ "OK", ++ 4, ++ {"string", "string", "string", "string"}, ++ "None.", ++ }}, ++ MessageEntry{ ++ "StateSensorWarning", ++ { ++ "Indicates that a state sensor has changed state to warning.", ++ "%1 of %2 state sensor changed from %3 to %4.", ++ "Warning", ++ "Warning", ++ 4, ++ {"string", "string", "string", "string"}, ++ "Check sensor subsystem for errors.", ++ }}, ++ MessageEntry{ ++ "StateSensorCritical", ++ { ++ "Indicates that a state sensor has changed state to critical.", ++ "%1 of %2 state sensor changed from %3 to %4.", ++ "Critical", ++ "Critical", ++ 4, ++ {"string", "string", "string", "string"}, ++ "Check sensor subsystem for errors.", ++ }}, + MessageEntry{"SystemInterfaceDisabledProvisioned", + { + "Indicates that the system interface is in the disabled " +@@ -2423,6 +2456,5 @@ constexpr std::array<MessageEntry, 188> registry = { + {"string"}, + "None.", + }}, +- + }; + } // namespace redfish::message_registries::openbmc +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0016-Fix-bmcweb-crashes-if-socket-directory-not-present.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0016-Fix-bmcweb-crashes-if-socket-directory-not-present.patch new file mode 100644 index 000000000..bc023839f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0016-Fix-bmcweb-crashes-if-socket-directory-not-present.patch @@ -0,0 +1,44 @@ +From 48fe2a68d634970795f9ff13903afbedca801088 Mon Sep 17 00:00:00 2001 +From: Nidhin MS <nidhin.ms@intel.com> +Date: Wed, 14 Apr 2021 11:28:44 +0530 +Subject: [PATCH] Fix: bmcweb crashes if socket directory not present + +When trying to mount virtual media image bmcweb tries to create unix +socket and if the parent directory does not exist +stream_protocol::acceptor throws error and bmcweb crashes. Fix the same + +Tested: +Removed directory and mounted the vm image. bmcweb crash was not +observed + +Change-Id: I3aea1d8e197c06238f425a97435c01d3c80552a9 +Signed-off-by: Nidhin MS <nidhin.ms@intel.com> +--- + include/nbd_proxy.hpp | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/include/nbd_proxy.hpp b/include/nbd_proxy.hpp +index 7b90e90..3b28823 100644 +--- a/include/nbd_proxy.hpp ++++ b/include/nbd_proxy.hpp +@@ -397,6 +397,17 @@ inline void requestRoutes(App& app) + // If the socket file exists (i.e. after bmcweb crash), + // we cannot reuse it. + std::remove((*socketValue).c_str()); ++ std::filesystem::path socketPath(*socketValue); ++ std::error_code fsErr; ++ if (!std::filesystem::exists(socketPath.parent_path(), ++ fsErr)) ++ { ++ BMCWEB_LOG_ERROR ++ << "VirtualMedia socket directory not present. " ++ << socketPath.parent_path(); ++ conn.close("Unable to create unix socket"); ++ return; ++ } + + sessions[&conn] = std::make_shared<NbdProxyServer>( + conn, *socketValue, *endpointValue, +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0017-Add-msg-registry-for-subscription-related-actions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0017-Add-msg-registry-for-subscription-related-actions.patch new file mode 100644 index 000000000..35ac7a114 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0017-Add-msg-registry-for-subscription-related-actions.patch @@ -0,0 +1,81 @@ +From 40895934bdedb978e8cfd47930ae5a190e19b440 Mon Sep 17 00:00:00 2001 +From: Ayushi Smriti <smriti.ayushi@intel.com> +Date: Mon, 10 May 2021 12:32:30 +0530 +Subject: [PATCH] Add msg registry for subscription related actions + +For subscription event message log purpose, added message registry +entry for event service subscription related actions- add, update +and delete. + +Tested: + - Message registry entry appears in the log for the corresponding + subscription action. + +Signed-off-by: AppaRao Puli <apparao.puli@intel.com> +Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com> +--- + .../registries/openbmc_message_registry.hpp | 41 ++++++++++++++++++- + 1 file changed, 40 insertions(+), 1 deletion(-) + +diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp +index 87b2913..1e493ca 100644 +--- a/redfish-core/include/registries/openbmc_message_registry.hpp ++++ b/redfish-core/include/registries/openbmc_message_registry.hpp +@@ -29,7 +29,7 @@ const Header header = { + "0.2.0", + "OpenBMC", + }; +-constexpr std::array<MessageEntry, 191> registry = { ++constexpr std::array<MessageEntry, 194> registry = { + MessageEntry{ + "ADDDCCorrectable", + { +@@ -417,6 +417,45 @@ constexpr std::array<MessageEntry, 191> registry = { + {}, + "None.", + }}, ++ MessageEntry{"EventSubscriptionAdded", ++ { ++ "Indicates that an Event subscription with specific " ++ "id was added.", ++ "Event subscription with id %1 was added.", ++ "OK", ++ "OK", ++ 1, ++ { ++ "string", ++ }, ++ "None.", ++ }}, ++ MessageEntry{"EventSubscriptionRemoved", ++ { ++ "Indicates that an Event subscription with specific " ++ "id was removed.", ++ "Event subscription with id %1 was removed.", ++ "OK", ++ "OK", ++ 1, ++ { ++ "string", ++ }, ++ "None.", ++ }}, ++ MessageEntry{"EventSubscriptionUpdated", ++ { ++ "Indicates that an Event subscription with specific " ++ " id was updated.", ++ "Event subscription with id %1 was updated.", ++ "OK", ++ "OK", ++ 1, ++ { ++ "string", ++ }, ++ "None.", ++ }}, + MessageEntry{"FanInserted", + { + "Indicates that a system fan has been inserted.", +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0018-bmcweb-Add-BMC-Time-update-log-to-the-registry.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0018-bmcweb-Add-BMC-Time-update-log-to-the-registry.patch new file mode 100644 index 000000000..54636cb3d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0018-bmcweb-Add-BMC-Time-update-log-to-the-registry.patch @@ -0,0 +1,77 @@ +From eeac51ebaaad82bb4ac65a029c81c221e32b33ea Mon Sep 17 00:00:00 2001 +From: mansijos <mansi.joshi@intel.com> +Date: Wed, 26 May 2021 17:40:04 +0530 +Subject: [PATCH] Add BMC Time update log to the registry + +Add message in registry to log an event that indicates BMC time +is set via NTP, Host or Manually. +During early stage of system boot if any critical events occur, +they are getting logged with 1970 timestamp till the time BMC +time update happens. This is expected behavior, but to call it out +explicitly it is good to log when BMC time is updated. + +Tested: +Built and validator passes. +Confirmed that the event is getting logged correctly in Redfish. + +Signed-off-by: mansijos <mansi.joshi@intel.com> +--- + .../registries/openbmc_message_registry.hpp | 35 ++++++++++++++++++- + 1 file changed, 34 insertions(+), 1 deletion(-) + +diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp +index 1e493ca..967713f 100644 +--- a/redfish-core/include/registries/openbmc_message_registry.hpp ++++ b/redfish-core/include/registries/openbmc_message_registry.hpp +@@ -29,7 +29,7 @@ const Header header = { + "0.2.0", + "OpenBMC", + }; +-constexpr std::array<MessageEntry, 194> registry = { ++constexpr std::array<MessageEntry, 197> registry = { + MessageEntry{ + "ADDDCCorrectable", + { +@@ -286,6 +286,39 @@ constexpr std::array<MessageEntry, 194> registry = { + {}, + "None.", + }}, ++ MessageEntry{"BMCTimeUpdatedViaHost", ++ { ++ "Indicates that BMC time has been set via Host.", ++ "BMC time has been set via Host. " ++ "Date Time is set to %1 from %2.", ++ "OK", ++ "OK", ++ 2, ++ {"string", "string"}, ++ "None.", ++ }}, ++ MessageEntry{"BMCTimeUpdatedManually", ++ { ++ "Indicates that BMC time has been set Manually.", ++ "BMC time has been set Manually. " ++ "Date Time is set to %1 from %2.", ++ "OK", ++ "OK", ++ 2, ++ {"string", "string"}, ++ "None.", ++ }}, ++ MessageEntry{"BMCTimeUpdatedViaNTP", ++ { ++ "Indicates that BMC time has been set via NTP.", ++ "BMC time has been set via NTP. " ++ "Date Time is set to %1 from %2.", ++ "OK", ++ "OK", ++ 2, ++ {"string", "string"}, ++ "None.", ++ }}, + MessageEntry{"ChassisIntrusionDetected", + { + "Indicates that a physical security event " +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0019-Add-generic-message-PropertySizeExceeded.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0019-Add-generic-message-PropertySizeExceeded.patch new file mode 100644 index 000000000..522f04886 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0019-Add-generic-message-PropertySizeExceeded.patch @@ -0,0 +1,120 @@ +From 6bc3ec77e062e8f2108f755e9f0089a014031f91 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Wed, 6 Oct 2021 21:51:16 +0000 +Subject: [PATCH] Add generic message - PropertySizeExceeded + +Adding a generic error message "PropertySizeExceeded" +to address properties which exceed there defined size limit. + +Tested: +No functional change. Build passed. +Verified by explicitly sending this message as a response. + +Change-Id: I0e9f85f82a69c598e169fc8e9a68c3f66c0084d8 +Signed-off-by: Nitin Wankhade <nitinx.arunrao.wankhade@intel.com> +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +--- + redfish-core/include/error_messages.hpp | 12 +++++++++ + .../registries/base_message_registry.hpp | 17 +++++++++++- + redfish-core/src/error_messages.cpp | 27 +++++++++++++++++++ + 3 files changed, 55 insertions(+), 1 deletion(-) + +diff --git a/redfish-core/include/error_messages.hpp b/redfish-core/include/error_messages.hpp +index 3d11cc4..051cff1 100644 +--- a/redfish-core/include/error_messages.hpp ++++ b/redfish-core/include/error_messages.hpp +@@ -222,6 +222,18 @@ nlohmann::json propertyValueFormatError(const std::string& arg1, + void propertyValueFormatError(crow::Response& res, const std::string& arg1, + const std::string& arg2); + ++/** ++ * @brief Formats PropertySizeExceeded message into JSON ++ * Message body: "The property <arg1> is too long. The value exceeds its size ++ * limit." ++ * ++ * @param[in] arg1 Parameter of message that will replace %1 in its body. ++ * ++ * @returns Message PropertySizeExceeded formatted to JSON */ ++nlohmann::json propertySizeExceeded(const std::string& arg1); ++ ++void propertySizeExceeded(crow::Response& res, const std::string& arg1); ++ + /** + * @brief Formats PropertyValueNotInList message into JSON + * Message body: "The value <arg1> for the property <arg2> is not in the list of +diff --git a/redfish-core/include/registries/base_message_registry.hpp b/redfish-core/include/registries/base_message_registry.hpp +index 702cd6f..193df16 100644 +--- a/redfish-core/include/registries/base_message_registry.hpp ++++ b/redfish-core/include/registries/base_message_registry.hpp +@@ -22,7 +22,7 @@ const Header header = { + constexpr const char* url = + "https://redfish.dmtf.org/registries/Base.1.11.0.json"; + +-constexpr std::array<MessageEntry, 93> registry = { ++constexpr std::array<MessageEntry, 94> registry = { + MessageEntry{ + "AccessDenied", + { +@@ -692,6 +692,21 @@ constexpr std::array<MessageEntry, 93> registry = { + "Remove the property from the request body and resubmit " + "the request if the operation failed.", + }}, ++ MessageEntry{"PropertySizeExceeded", ++ { ++ "Indicates that a given property exceeds the size " ++ "limit imposed.", ++ "The property %1 is too long. The value exceeds " ++ "its size limit.", ++ "Warning", ++ "Warning", ++ 1, ++ { ++ "string", ++ }, ++ "Correct the value for the property in the request body " ++ "and resubmit the request if the operation failed.", ++ }}, + MessageEntry{"PropertyUnknown", + { + "Indicates that an unknown property was included in the " +diff --git a/redfish-core/src/error_messages.cpp b/redfish-core/src/error_messages.cpp +index 9c28e8f..854a1a5 100644 +--- a/redfish-core/src/error_messages.cpp ++++ b/redfish-core/src/error_messages.cpp +@@ -514,6 +514,33 @@ void propertyValueFormatError(crow::Response& res, const std::string& arg1, + addMessageToJson(res.jsonValue, propertyValueFormatError(arg1, arg2), arg2); + } + ++/** ++ * @internal ++ * @brief Formats PropertySizeExceeded message into JSON for the specified ++ * property ++ * ++ * See header file for more information ++ * @endinternal ++ */ ++nlohmann::json propertySizeExceeded(const std::string& arg1) ++{ ++ return nlohmann::json{ ++ {"@odata.type", "#Message.v1_1_1.Message"}, ++ {"MessageId", "Base.1.8.1.PropertySizeExceeded"}, ++ {"Message", "The property " + arg1 + ++ " is too long. The value exceeds its size limit."}, ++ {"MessageArgs", {arg1}}, ++ {"MessageSeverity", "Warning"}, ++ {"Resolution", "Correct the value for the property in the request body " ++ "and resubmit the request if the operation failed."}}; ++} ++ ++void propertySizeExceeded(crow::Response& res, const std::string& arg1) ++{ ++ res.result(boost::beast::http::status::bad_request); ++ addMessageToJson(res.jsonValue, propertySizeExceeded(arg1), arg1); ++} ++ + /** + * @internal + * @brief Formats PropertyValueNotInList message into JSON for the specified +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0020-Redfish-Deny-set-AccountLockDuration-to-zero.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0020-Redfish-Deny-set-AccountLockDuration-to-zero.patch new file mode 100644 index 000000000..cc9da3b8b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0020-Redfish-Deny-set-AccountLockDuration-to-zero.patch @@ -0,0 +1,85 @@ +From f75efac9eebea8bf8f548d10a8cbafa28f556a8f Mon Sep 17 00:00:00 2001 +From: Meera-Katta <meerax.katta@linux.intel.com> +Date: Wed, 7 Jul 2021 13:19:09 +0000 +Subject: [PATCH] Redfish: Deny set AccountLockDuration to zero +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Issue: Redfish schema says, no lockout shall occur in case of Account +LockoutDuration value is zero. But Linux PAM module documentation says, if +account lockout value is zero, account will be locked out indefinitely +after the number of failed login attempts. As per the current +implementation user can write any value into the PAM module. If user tried +to set unlock timeout value to zero, account will be locked out +indefinitely until administrator explicitly reenables it. + +Workaround: Denying user to set AccountLockDuration to zero from Redfish. +Setting ‘AccountLockDuration’ to 0 will be permitted only after +‘AccountLockoutCounterResetEnabled’ support is added. +Otherwise,account will be locked permanently after the AccountLockoutDuration +if ‘AccountLockDuration’ is set to zero, while +AccountLockoutThreshold is non zero. If someone wants no account lockout +irrespective of number of failed login attempts, it can be still achieved by +setting ‘AccountLockoutThreshold’ to zero +(instead of trying to set ‘AccountLockDuration’ to zero.) + +Tested: +1) Redfish Service Validator passed for this change. +2) Verified from Redfish +PATCH : https://<BMC IP>/redfish/v1/AccountService +Body: +{"AccountLockoutDuration":0} + +Response: +{ + "AccountLockoutDuration@Message.ExtendedInfo": [ + { + "@odata.type": "#Message.v1_1_1.Message", + "Message": "The value unlockTimeout for the property + AccountLockoutDuration is not in the list of acceptable values.", + "MessageArgs": [ + "unlockTimeout", + "AccountLockoutDuration" + ], + "MessageId": "Base.1.8.1.PropertyValueNotInList", + "MessageSeverity": "Warning", + "Resolution": "Choose a value from the enumeration list that the + implementation can support and resubmit the request if the + operation failed." + } + ] +} + +Signed-off-by: Meera-Katta <meerax.katta@linux.intel.com> +--- + redfish-core/lib/account_service.hpp | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp +index e6fe205..42085fa 100644 +--- a/redfish-core/lib/account_service.hpp ++++ b/redfish-core/lib/account_service.hpp +@@ -1448,6 +1448,19 @@ inline void requestAccountServiceRoutes(App& app) + + if (unlockTimeout) + { ++ // Account will be locked permanently after the N number ++ // of failed login attempts if we set unlockTimeout value ++ // to be 0. ++ if (unlockTimeout.value() == 0) ++ { ++ BMCWEB_LOG_INFO ++ << "Unlock timeout value must be greater" ++ "than zero"; ++ messages::propertyValueNotInList(asyncResp->res, ++ "unlockTimeout", ++ "AccountLockoutDuration"); ++ return; ++ } + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec) { + if (ec) +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0023-Add-get-IPMI-session-id-s-to-Redfish.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0023-Add-get-IPMI-session-id-s-to-Redfish.patch new file mode 100644 index 000000000..b3feee39a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0023-Add-get-IPMI-session-id-s-to-Redfish.patch @@ -0,0 +1,390 @@ +From 5c79e34be9357c2a2cd9bac61cd0162dbd342a2d Mon Sep 17 00:00:00 2001 +From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +Date: Fri, 30 Jul 2021 17:33:16 +0000 +Subject: [PATCH] Add/get IPMI session id's to Redfish + +As per existing implementation, Redfish supports to get only sessions +created on Redfish & EWS. But as per Redfish schema Redfish should +include to get IPMI sessions as well. +So add support to display IPMI session Id's as well on Redfish. +This commit will not impact any functionality/behavior of existing code. +Below functionalities implemented in this commit. +1. Get IPMI session collection +2. Get individual IPMI session information +3. Delete IPMI sessions - Respond with not supported as we can't delete + IPMI sessions from Redfish interface + +Tested: +1. Verified redfish validator passed with active IPMI session. +2. Get session details from Redfish +GET: https://<BMC-IP>/redfish/v1/SessionService/Sessions +Response: +{ + "@odata.id": "/redfish/v1/SessionService/Sessions/", + "@odata.type": "#SessionCollection.SessionCollection", + "Description": "Session Collection", + "Members": [ + { + "@odata.id": "/redfish/v1/SessionService/Sessions/TlFPbR9ZIn" + }, + { + "@odata.id": "/redfish/v1/SessionService/Sessions/184U3014ub" + }, + { + "@odata.id": "/redfish/v1/SessionService/Sessions/cV0xi5QoPy" + }, + { + "@odata.id": "/redfish/v1/SessionService/Sessions/8f6234d7_81" + } + ], + "Members@odata.count": 4, + "Name": "Session Collection" +} + +3. Get session details from RedFish +Case 1: RedFish session +GET: https://<BMC-IP>/redfish/v1/SessionService/Sessions/TlFPbR9ZIn +Response: +{ + "@odata.id": "/redfish/v1/SessionService/Sessions/TlFPbR9ZIn", + "@odata.type": "#Session.v1_3_0.Session", + "ClientOriginIPAddress": "::ffff:10.213.91.40", + "Description": "Manager User Session", + "Id": "TlFPbR9ZIn", + "Name": "User Session", + "UserName": "root" +} +Case 2: IPMI session +Verified and displayed IPMI session details on RedFish. +GET: https://<BMC-IP>/redfish/v1/SessionService/Sessions/8f6234d7_81 +Response: +{ + "@odata.id": "/redfish/v1/SessionService/Sessions/8f6234d7_81", + "@odata.type": "#Session.v1_3_0.Session", + "ClientOriginIPAddress": "xx.xx.xx.xx", + "Description": "Manager User Session", + "Id": "8f6234d7_81", + "Name": "User Session", + "UserName": "root" +} +4. Delete IPMI session: +Verified IPMI session is not allowed to delete from Redfish +DELETE: https://<BMC-IP>/redfish/v1/SessionService/Sessions/8f6234d7_81 +Response: +{ + "error": { + "@Message.ExtendedInfo": [ + { + "@odata.type": "#Message.v1_1_1.Message", + "Message": "The action deleting IPMI session from + Redfish is not supported by the resource.", + "MessageArgs": [ + "deleting IPMI session from Redfish" + ], + "MessageId": "Base.1.8.1.ActionNotSupported", + "MessageSeverity": "Critical", + "Resolution": "The action supplied cannot be resubmitted + to the implementation. Perhaps the action was invalid, + the wrong resource was the target or the implementation + documentation may be of assistance." + } + ], + "code": "Base.1.8.1.ActionNotSupported", + "message": "The action deleting IPMI session from Redfish is not + supported by the resource." + } +} +5. Delete RedFish session +Result: successfully deleted valid RedFish session. + +Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +--- + redfish-core/lib/redfish_sessions.hpp | 244 +++++++++++++++++++++++--- + 1 file changed, 222 insertions(+), 22 deletions(-) + +diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp +index 929e0c8..3c7a968 100644 +--- a/redfish-core/lib/redfish_sessions.hpp ++++ b/redfish-core/lib/redfish_sessions.hpp +@@ -56,14 +56,127 @@ inline void requestRoutesSession(App& app) + auto session = persistent_data::SessionStore::getInstance() + .getSessionByUid(sessionId); + +- if (session == nullptr) ++ if (session) + { +- messages::resourceNotFound(asyncResp->res, "Session", +- sessionId); ++ fillSessionObject(asyncResp->res, *session); + return; + } + +- fillSessionObject(asyncResp->res, *session); ++ std::array<std::string, 1> interfaces = { ++ "xyz.openbmc_project.Ipmi.SessionInfo"}; ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, sessionId](const boost::system::error_code ec, ++ const GetSubTreeType& subtree) { ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Error in querying GetSubTree with " ++ "Object Mapper. " ++ << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ if (subtree.size() == 0) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Can't find Session Info Attributes!"; ++ messages::resourceNotFound(asyncResp->res, ++ "Session", sessionId); ++ return; ++ } ++ bool ipmiSessionFound = false; ++ std::string ipmiSessionService; ++ std::string ipmiSessionInfPath; ++ for (const auto& [ipmiSessionPath, object] : subtree) ++ { ++ if (ipmiSessionPath.empty() || object.empty()) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Session Info Attributes mapper error!"; ++ continue; ++ } ++ if (!boost::ends_with(ipmiSessionPath, sessionId)) ++ { ++ continue; ++ } ++ ipmiSessionFound = true; ++ ipmiSessionService = object[0].first; ++ ipmiSessionInfPath = ipmiSessionPath; ++ break; ++ } ++ if (!ipmiSessionFound) ++ { ++ messages::resourceNotFound(asyncResp->res, ++ "Session", sessionId); ++ return; ++ } ++ if (ipmiSessionService.empty()) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Session Info Attributes mapper " ++ "error!"; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, sessionId]( ++ const boost::system::error_code ec, ++ const std::vector<std::pair< ++ std::string, ++ std::variant<std::monostate, std::string, ++ uint32_t>>>& properties) { ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Error in querying Session " ++ "Info State property " ++ << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ std::string userName = ""; ++ uint32_t remoteIpAddr; ++ try ++ { ++ sdbusplus::unpackProperties( ++ properties, "Username", userName, ++ "RemoteIPAddr", remoteIpAddr); ++ asyncResp->res.jsonValue["Id"] = sessionId; ++ asyncResp->res.jsonValue["UserName"] = ++ userName; ++ asyncResp->res.jsonValue["@odata.id"] = ++ "/redfish/v1/SessionService/" ++ "Sessions/" + ++ sessionId; ++ asyncResp->res.jsonValue["@odata.type"] = ++ "#Session.v1_3_0.Session"; ++ asyncResp->res.jsonValue["Name"] = ++ "User Session"; ++ asyncResp->res.jsonValue["Description"] = ++ "Manager User Session"; ++ struct in_addr ipAddr; ++ ipAddr.s_addr = remoteIpAddr; ++ asyncResp->res ++ .jsonValue["ClientOriginIPAddress"] = ++ inet_ntoa(ipAddr); ++ } ++ catch (const sdbusplus::exception:: ++ UnpackPropertyError& error) ++ { ++ BMCWEB_LOG_ERROR << error.what(); ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ return; ++ }, ++ ipmiSessionService, ipmiSessionInfPath, ++ "org.freedesktop.DBus.Properties", "GetAll", ++ "xyz.openbmc_project.Ipmi.SessionInfo"); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, ++ interfaces); + }); + + BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/") +@@ -75,34 +188,79 @@ inline void requestRoutesSession(App& app) + auto session = persistent_data::SessionStore::getInstance() + .getSessionByUid(sessionId); + +- if (session == nullptr) +- { +- messages::resourceNotFound(asyncResp->res, "Session", +- sessionId); +- return; +- } +- + // Perform a proper ConfigureSelf authority check. If a + // session is being used to DELETE some other user's session, + // then the ConfigureSelf privilege does not apply. In that + // case, perform the authority check again without the user's + // ConfigureSelf privilege. +- if (session->username != req.session->username) ++ if (session) + { +- Privileges effectiveUserPrivileges = +- redfish::getUserPrivileges(req.userRole); +- +- if (!effectiveUserPrivileges.isSupersetOf( +- {"ConfigureUsers"})) ++ if (session->username != req.session->username) + { +- messages::insufficientPrivilege(asyncResp->res); +- return; ++ Privileges effectiveUserPrivileges = ++ redfish::getUserPrivileges(req.userRole); ++ ++ if (!effectiveUserPrivileges.isSupersetOf( ++ {"ConfigureUsers"})) ++ { ++ messages::insufficientPrivilege(asyncResp->res); ++ return; ++ } + } ++ persistent_data::SessionStore::getInstance().removeSession( ++ session); ++ messages::success(asyncResp->res); ++ return; + } + +- persistent_data::SessionStore::getInstance().removeSession( +- session); +- messages::success(asyncResp->res); ++ std::array<std::string, 1> interfaces = { ++ "xyz.openbmc_project.Ipmi.SessionInfo"}; ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, ++ sessionId](const boost::system::error_code ec, ++ const std::vector<std::string>& ifaceList) { ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Error in querying GetSubTreePaths " ++ "with Object Mapper. " ++ << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ if (ifaceList.size() == 0) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Can't find Session Info Attributes!"; ++ return; ++ } ++ bool ipmiSessionFound = false; ++ for (const std::string& ipmiSessionPath : ifaceList) ++ { ++ if (!boost::ends_with(ipmiSessionPath, sessionId)) ++ { ++ continue; ++ } ++ ipmiSessionFound = true; ++ break; ++ } ++ if (ipmiSessionFound) ++ { ++ BMCWEB_LOG_DEBUG << "Deleting IPMI session from " ++ "Redfish is not allowed."; ++ messages::actionNotSupported( ++ asyncResp->res, ++ "deleting IPMI session from Redfish"); ++ return; ++ } ++ messages::resourceNotFound(asyncResp->res, "Session", ++ sessionId); ++ return; ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", ++ 0, interfaces); + }); + + BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/") +@@ -131,6 +289,48 @@ inline void requestRoutesSession(App& app) + "/redfish/v1/SessionService/Sessions/"; + asyncResp->res.jsonValue["Name"] = "Session Collection"; + asyncResp->res.jsonValue["Description"] = "Session Collection"; ++ ++ std::array<std::string, 1> interfaces = { ++ "xyz.openbmc_project.Ipmi.SessionInfo"}; ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec, ++ const std::vector<std::string>& ifaceList) { ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Error in querying GetSubTreePaths " ++ "with Object Mapper. " ++ << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ if (ifaceList.size() == 0) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Can't find Session Info Attributes!"; ++ return; ++ } ++ for (const std::string& ipmiSessionPath : ifaceList) ++ { ++ std::filesystem::path filePath(ipmiSessionPath); ++ std::string ipmiSessionID = ++ filePath.has_filename() ? filePath.filename() ++ : ""; ++ if (!ipmiSessionID.empty() && ipmiSessionID != "0") ++ { ++ asyncResp->res.jsonValue["Members"].push_back( ++ {{"@odata.id", ++ "/redfish/v1/SessionService/Sessions/" + ++ ipmiSessionID}}); ++ } ++ } ++ asyncResp->res.jsonValue["Members@odata.count"] = ++ asyncResp->res.jsonValue["Members"].size(); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", ++ 0, interfaces); + }); + + BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/") +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0024-Add-count-sensor-type.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0024-Add-count-sensor-type.patch new file mode 100644 index 000000000..22ae05fa3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0024-Add-count-sensor-type.patch @@ -0,0 +1,29 @@ +From 94a0ae774933b7801d0c8d843b3ac3a39a5e5646 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Adrian=20Ambro=C5=BCewicz?= <adrian.ambrozewicz@intel.com> +Date: Fri, 30 Jul 2021 15:25:29 +0200 +Subject: [PATCH] Add 'count' sensor type + +PMT exposes data mainly in raw counter formats. This change makes +bmcweb aware of new sensor type. + +Testing: +- values of type 'count' from PMT exposed successfully on Redfish +--- + redfish-core/lib/sensors.hpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp +index 45a1eb6..7405e5a 100644 +--- a/redfish-core/lib/sensors.hpp ++++ b/redfish-core/lib/sensors.hpp +@@ -63,6 +63,7 @@ static const boost::container::flat_map<std::string_view, + {node::sensors, + {"/xyz/openbmc_project/sensors/power", + "/xyz/openbmc_project/sensors/current", ++ "/xyz/openbmc_project/sensors/count", + "/xyz/openbmc_project/sensors/airflow", + #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM + "/xyz/openbmc_project/sensors/voltage", +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0025-Add-Model-CoreCount-to-ProcessorSummary.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0025-Add-Model-CoreCount-to-ProcessorSummary.patch new file mode 100644 index 000000000..edf4d219e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0025-Add-Model-CoreCount-to-ProcessorSummary.patch @@ -0,0 +1,288 @@ +From f4f15a52610d1a199ddac948c8f849df05d86151 Mon Sep 17 00:00:00 2001 +From: Ali Ahmed <ama213000@gmail.com> +Date: Fri, 3 Sep 2021 02:33:43 -0500 +Subject: [PATCH] Add Model & CoreCount to ProcessorSummary + +In Redfish ComputerSystem schema, the ProcessorSummary parameter +lists summary information of the Processors on the system. This commit +adds the 'Model' and 'CoreCount' properties to ProcessorSummary. + +If the CPU Models are different, then the 'Model' field takes the first +entry in alphabetical order. + +Testing: +1. Redfish Validator Testing successfully passed. +2. Curl testing: + +curl -k -H "X-Auth-Token: $tok" https://$bmc/redfish/v1/Systems/system + +... + "ProcessorSummary": { + "CoreCount": 24, + "Count": 2, + "Model": "test_name", + "Status": { + "Health": "OK", + "HealthRollup": "OK", + "State": "Disabled" + } + }, +... + +Change-Id: I39cbf6ed35c35ce3a3551c9689237d5023775326 +Signed-off-by: Ali Ahmed <ama213000@gmail.com> +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +--- + redfish-core/lib/systems.hpp | 229 ++++++++++++++++++++++------------- + 1 file changed, 147 insertions(+), 82 deletions(-) + +diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp +index 680a0ee..3b5f9e4 100644 +--- a/redfish-core/lib/systems.hpp ++++ b/redfish-core/lib/systems.hpp +@@ -139,6 +139,152 @@ inline void + } + } + ++inline void getProcessorProperties( ++ const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& service, ++ const std::string& path, ++ const std::vector<std::pair< ++ std::string, std::variant<std::string, uint64_t, uint32_t, uint16_t>>>& ++ properties) ++{ ++ ++ BMCWEB_LOG_DEBUG << "Got " << properties.size() << " Cpu properties."; ++ ++ auto getCpuPresenceState = ++ [aResp](const boost::system::error_code ec3, ++ const std::variant<bool>& cpuPresenceCheck) { ++ if (ec3) ++ { ++ BMCWEB_LOG_ERROR << "DBUS response error " << ec3; ++ return; ++ } ++ modifyCpuPresenceState(aResp, cpuPresenceCheck); ++ }; ++ ++ auto getCpuFunctionalState = ++ [aResp](const boost::system::error_code ec3, ++ const std::variant<bool>& cpuFunctionalCheck) { ++ if (ec3) ++ { ++ BMCWEB_LOG_ERROR << "DBUS response error " << ec3; ++ return; ++ } ++ modifyCpuFunctionalState(aResp, cpuFunctionalCheck); ++ }; ++ ++ // Get the Presence of CPU ++ crow::connections::systemBus->async_method_call( ++ std::move(getCpuPresenceState), service, path, ++ "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.Inventory.Item", "Present"); ++ ++ // Get the Functional State ++ crow::connections::systemBus->async_method_call( ++ std::move(getCpuFunctionalState), service, path, ++ "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional"); ++ ++ for (const auto& property : properties) ++ { ++ if (property.first == "Family") ++ { ++ // Get the CPU Model ++ const std::string* modelStr = ++ std::get_if<std::string>(&property.second); ++ if (!modelStr) ++ { ++ BMCWEB_LOG_DEBUG << "Failed to get CPU Family"; ++ // Skip it and continue with other properties ++ continue; ++ } ++ if ((*modelStr).size() < 1) ++ { ++ BMCWEB_LOG_DEBUG << "Empty CPU Family info, skipping..."; ++ continue; ++ } ++ nlohmann::json& prevModel = ++ aResp->res.jsonValue["ProcessorSummary"]["Model"]; ++ std::string* prevModelPtr = prevModel.get_ptr<std::string*>(); ++ ++ // If CPU Models are different, use the first entry in ++ // alphabetical order ++ ++ // If Model has never been set ++ // before, set it to *modelStr ++ if (prevModelPtr == nullptr) ++ { ++ prevModel = *modelStr; ++ } ++ // If Model has been set before, only change if new Model is ++ // higher in alphabetical order ++ else ++ { ++ if (*modelStr < *prevModelPtr) ++ { ++ prevModel = *modelStr; ++ } ++ } ++ } ++ else if (property.first == "CoreCount") ++ { ++ // Get CPU CoreCount and add it to the total ++ const uint16_t* coreCountVal = ++ std::get_if<uint16_t>(&property.second); ++ ++ if (!coreCountVal) ++ { ++ BMCWEB_LOG_DEBUG << "Failed to get CPU Core count"; ++ // Skip it and continue with other properties ++ continue; ++ } ++ ++ nlohmann::json& coreCount = ++ aResp->res.jsonValue["ProcessorSummary"]["CoreCount"]; ++ uint64_t* coreCountPtr = coreCount.get_ptr<uint64_t*>(); ++ ++ if (coreCountPtr == nullptr) ++ { ++ coreCount = *coreCountVal; ++ } ++ else ++ { ++ *coreCountPtr += *coreCountVal; ++ } ++ } ++ } ++} ++ ++/* ++ * @brief Get ProcessorSummary fields ++ * ++ * @param[in] aResp Shared pointer for completing asynchronous calls ++ * @param[in] service dbus service for Cpu Information ++ * @param[in] path dbus path for Cpu ++ * ++ * @return None. ++ */ ++inline void getProcessorSummary(const std::shared_ptr<bmcweb::AsyncResp>& aResp, ++ const std::string& service, ++ const std::string& path) ++{ ++ ++ crow::connections::systemBus->async_method_call( ++ [aResp, service, ++ path](const boost::system::error_code ec2, ++ const std::vector<std::pair< ++ std::string, std::variant<std::string, uint64_t, uint32_t, ++ uint16_t>>>& properties) { ++ if (ec2) ++ { ++ BMCWEB_LOG_ERROR << "DBUS response error " << ec2; ++ messages::internalError(aResp->res); ++ return; ++ } ++ getProcessorProperties(aResp, service, path, properties); ++ }, ++ service, path, "org.freedesktop.DBus.Properties", "GetAll", ++ "xyz.openbmc_project.Inventory.Item.Cpu"); ++} ++ + /* + * @brief Retrieves computer system properties over dbus + * +@@ -309,88 +455,7 @@ inline void + BMCWEB_LOG_DEBUG + << "Found Cpu, now get its properties."; + +- crow::connections::systemBus->async_method_call( +- [aResp, service{connection.first}, +- path](const boost::system::error_code ec2, +- const std::vector< +- std::pair<std::string, VariantType>>& +- properties) { +- if (ec2) +- { +- BMCWEB_LOG_ERROR +- << "DBUS response error " << ec2; +- messages::internalError(aResp->res); +- return; +- } +- BMCWEB_LOG_DEBUG << "Got " +- << properties.size() +- << " Cpu properties."; +- +- auto getCpuPresenceState = +- [aResp]( +- const boost::system::error_code ec3, +- const std::variant<bool>& +- cpuPresenceCheck) { +- if (ec3) +- { +- BMCWEB_LOG_ERROR +- << "DBUS response error " +- << ec3; +- return; +- } +- modifyCpuPresenceState( +- aResp, cpuPresenceCheck); +- }; +- +- auto getCpuFunctionalState = +- [aResp]( +- const boost::system::error_code ec3, +- const std::variant<bool>& +- cpuFunctionalCheck) { +- if (ec3) +- { +- BMCWEB_LOG_ERROR +- << "DBUS response error " +- << ec3; +- return; +- } +- modifyCpuFunctionalState( +- aResp, cpuFunctionalCheck); +- }; +- +- // Get the Presence of CPU +- crow::connections::systemBus +- ->async_method_call( +- std::move(getCpuPresenceState), +- service, path, +- "org.freedesktop.DBus." +- "Properties", +- "Get", +- "xyz.openbmc_project.Inventory." +- "Item", +- "Present"); +- +- // Get the Functional State +- crow::connections::systemBus +- ->async_method_call( +- std::move(getCpuFunctionalState), +- service, path, +- "org.freedesktop.DBus." +- "Properties", +- "Get", +- "xyz.openbmc_project.State." +- "Decorator." +- "OperationalStatus", +- "Functional"); +- +- // Get the MODEL from +- // xyz.openbmc_project.Inventory.Decorator.Asset +- // support it later as Model is Empty +- // currently. +- }, +- connection.first, path, +- "org.freedesktop.DBus.Properties", "GetAll", +- "xyz.openbmc_project.Inventory.Item.Cpu"); ++ getProcessorSummary(aResp, connection.first, path); + + cpuHealth->inventory.emplace_back(path); + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0001-Define-Redfish-interface-Registries-Bios.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0001-Define-Redfish-interface-Registries-Bios.patch new file mode 100644 index 000000000..19a392873 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0001-Define-Redfish-interface-Registries-Bios.patch @@ -0,0 +1,875 @@ +From bde7f728d5a87522674bcd5515baaa02bf7b9373 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Fri, 4 Sep 2020 19:24:25 +0800 +Subject: [PATCH] Define Redfish interface "/Registries/Bios" and enable + Attributes property + +1. Define Redfish interface "/Registries/Bios" for BIOS Attribute Registry + RBC Daemon provide method to get BIOS attribute registry. +2. Eanble Attributes property for BIOS resource +3. Define Redfish interface "/Systems/system/Bios/Settings" for BIOS +settings +4. RBC daemon is at +https://gerrit.openbmc-project.xyz/#/c/openbmc/bios-settings-mgr/+/35563/ +5. IPMI command implementation is at +https://gerrit.openbmc-project.xyz/#/c/openbmc/intel-ipmi-oem/+/30827/ +6. Property design is at +https://github.com/openbmc/phosphor-dbus-interfaces/tree/master/xyz/openbmc_project/BIOSConfig +7. Design doc is at +https://github.com/openbmc/docs/blob/master/designs/remote-bios-configuration.md +8. There will be 95 test cases for this feature in the validation team. + +Tested: + +1. Use postman (Redfish tool) could get all the attributes in bios +resouce, get bios settings, get bios attribute +registry. +https://IP_ADDR/redfish/v1/Systems/system/Bios +{ + "@Redfish.Settings": { + "@odata.type": "#Settings.v1_3_0.Settings", + "SettingsObject": { + "@odata.id": "/redfish/v1/Systems/system/Bios/Settings" + } + }, + "@odata.id": "/redfish/v1/Systems/system/Bios", + "@odata.type": "#Bios.v1_1_0.Bios", + "Actions": { + "#Bios.ChangePassword": { + "target": "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword" + }, + "#Bios.ResetBios": { + "target": "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios" + } + }, + "AttributeRegistry": "BiosAttributeRegistry", + "Attributes": { + "attr0": "current value" + }, + "Description": "BIOS Configuration Service", + "Id": "BIOS", + "Links": { + "ActiveSoftwareImage": { + "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/bios_active" + }, + "SoftwareImages": [ + { + "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/bios_active" + } + ], + "SoftwareImages@odata.count": 1 + }, + "Name": "BIOS Configuration" +} + +Redfish interface: https://BMCIP/redfish/v1/Registries/BiosAttributeRegistry +{ + "@odata.id": "/redfish/v1/Registries/BiosAttributeRegistry", + "@odata.type": "#MessageRegistryFile.v1_1_0.MessageRegistryFile", + "Description": "BiosAttributeRegistry Message Registry File Location", + "Id": "BiosAttributeRegistry", + "Languages": [ + "en" + ], + "Languages@odata.count": 1, + "Location": [ + { + "Language": "en", + "Uri": "/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry" + } + ], + "Location@odata.count": 1, + "Name": "BiosAttributeRegistry Message Registry File", + "Registry": "BiosAttributeRegistry.1.0.0" +} + +Redfish interface: https://BMCIP/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry +{ + "@odata.id": "/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry", + "@odata.type": "#AttributeRegistry.v1_3_2.AttributeRegistry", + "Id": "BiosAttributeRegistry", + "Language": "en", + "Name": "Bios Attribute Registry", + "OwningEntity": "OpenBMC", + "RegistryEntries": { + "Attributes": [ + { + "AttributeName": "attr0", + "CurrentValue": "current value", + "DefaultValue": "default value", + "DisplayName": "display name for attr0", + "HelpText": "description for attr0", + "MenuPath": "./menu/path/for/attr0", + "ReadOnly": false, + "Type": "String", + "Value": [] + } + ] + }, + "RegistryVersion": "1.0.0" +} + +https://BMC_IPADDR/redfish/v1/Systems/system/Bios/Settings +{ + "@odata.id": "/redfish/v1/Systems/system/Bios/Settings", + "@odata.type": "#Bios.v1_1_0.Bios", + "AttributeRegistry": "BiosAttributeRegistry", + "Attributes": { + "QuietBoot": "0x0" + }, + "Id": "BiosSettingsV1", + "Name": "Bios Settings Version 1" +} + +2. Passed Validator check for bios resource and bios attribute registry +*** /redfish/v1/Systems/system/Bios +INFO - Type (#Bios.v1_1_0.Bios), GET SUCCESS (time: 1.57377) +INFO - PASS +*** /redfish/v1/Registries/BiosAttributeRegistry +INFO - Type (#MessageRegistryFile.v1_1_0.MessageRegistryFile), GET SUCCESS (time: 0.075438) +INFO - PASS +INFO - +*** /redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry +INFO - Type (#AttributeRegistry.v1_3_2.AttributeRegistry), GET SUCCESS (time: 0.075751) +INFO - PASS + +@odata.id /redfish/v1/Systems/system/Bios odata Exists PASS +@odata.type #Settings.v1_3_0.Settings odata Exists PASS +Links [JSON Object] Bios.v1_1_0.Links Yes complex +Links.ActiveSoftwareImage Link: /redfish/v1/UpdateService/FirmwareInventory/bios_active link to: SoftwareInventory Yes PASS +Links.SoftwareImages Array (size: 1) array of: SoftwareInventory Yes ... +Links.SoftwareImages[0] Link: /redfish/v1/UpdateService/FirmwareInventory/bios_active SoftwareInventory Yes PASS +Links.Oem - Resource.Oem No Optional +SoftwareImages@odata.count 1 odata Exists PASS +AttributeRegistry BiosAttributeRegistry string Yes PASS +Actions [JSON Object] Bios.v1_0_0.Actions Yes complex +Actions.#Bios.ResetBios Action - Yes PASS +Actions.#Bios.ChangePassword Action - Yes PASS +Attributes [JSON Object] Bios.v1_0_0.Attributes Yes complex +Attributes.attr0 current value primitive Yes PASS +Id BIOS string Yes PASS +Description BIOS Configuration Service string Yes PASS +Name BIOS Configuration string Yes PASS +Oem - Resource.Oem No Optional +@Redfish.Settings [JSON Object] Settings.Settings Yes complex +@Redfish.Settings.MaintenanceWindowResource - link to: ItemOrCollection No Optional +@Redfish.Settings.SupportedApplyTimes - string (enum) No Optional +@Redfish.Settings.Time - date No Optional +@Redfish.Settings.ETag - string No Optional +@Redfish.Settings.SettingsObject Link: /redfish/v1/Systems/system/Bios/Settings link to: Item Yes PASS +@Redfish.Settings.Messages - Message No Optional + +@odata.id /redfish/v1/Registries/BiosAttributeRegistry odata Exists PASS +@odata.type #MessageRegistryFile.v1_1_0.MessageRegistryFile odata Exists PASS +Languages@odata.count 1 odata Exists PASS +Location@odata.count 1 odata Exists PASS +Actions - MessageRegistryFile.v1_1_0.Actions No Optional +Languages Array (size: 1) string Yes ... +Languages[0] en string Yes PASS +Registry BiosAttributeRegistry.1.0.0 string Yes PASS +Location Array (size: 1) array of: Location Yes ... +Location[0] [JSON Object] Location Yes complex +Location[0].Language en string Yes PASS +Location[0].Uri /redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry string Yes PASS +Location[0].ArchiveUri - string No Optional +Location[0].PublicationUri - string No Optional +Location[0].ArchiveFile - string No Optional +Id BiosAttributeRegistry string Yes PASS +Description BiosAttributeRegistry Message Registry File Location string Yes PASS +Name BiosAttributeRegistry Message Registry File string Yes PASS +Oem - Resource.Oem No Optional + +@odata.id /redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry odata Exists PASS +@odata.type #AttributeRegistry.v1_3_2.AttributeRegistry odata Exists PASS +Actions - AttributeRegistry.v1_1_0.Actions No Optional +Language en string Yes PASS +RegistryVersion 1.0.0 string Yes PASS +OwningEntity OpenBMC string Yes PASS +SupportedSystems - SupportedSystems No Optional +RegistryEntries [JSON Object] AttributeRegistry.v1_0_0.RegistryEntries Yes complex +RegistryEntries.Attributes Array (size: 1) array of: Attributes Yes ... +RegistryEntries.Attributes[0] [JSON Object] Attributes Yes complex +RegistryEntries.Attributes[0].Oem - Resource.Oem No Optional +RegistryEntries.Attributes[0].ResetRequired - boolean No Optional +RegistryEntries.Attributes[0].UefiDevicePath - string No Optional +RegistryEntries.Attributes[0].UefiKeywordName - string No Optional +RegistryEntries.Attributes[0].UefiNamespaceId - string No Optional +RegistryEntries.Attributes[0].AttributeName attr0 string Yes PASS +RegistryEntries.Attributes[0].Type String string (enum) Yes PASS +RegistryEntries.Attributes[0].Value Array (size: 0) array of: AttributeValue Yes ... +RegistryEntries.Attributes[0].DisplayName display name for attr0 string Yes PASS +RegistryEntries.Attributes[0].HelpText description for attr0 string Yes PASS +RegistryEntries.Attributes[0].WarningText - string No Optional +RegistryEntries.Attributes[0].CurrentValue current value primitive Yes PASS +RegistryEntries.Attributes[0].DefaultValue default value primitive Yes PASS +RegistryEntries.Attributes[0].DisplayOrder - number No Optional +RegistryEntries.Attributes[0].MenuPath ./menu/path/for/attr0 string Yes PASS +RegistryEntries.Attributes[0].ReadOnly False boolean Yes PASS +RegistryEntries.Attributes[0].WriteOnly - boolean No Optional +RegistryEntries.Attributes[0].GrayOut - boolean No Optional +RegistryEntries.Attributes[0].Hidden - boolean No Optional +RegistryEntries.Attributes[0].Immutable - boolean No Optional +RegistryEntries.Attributes[0].IsSystemUniqueProperty - boolean No Optional +RegistryEntries.Attributes[0].MaxLength - number No Optional +RegistryEntries.Attributes[0].MinLength - number No Optional +RegistryEntries.Attributes[0].ScalarIncrement - number No Optional +RegistryEntries.Attributes[0].UpperBound - number No Optional +RegistryEntries.Attributes[0].LowerBound - number No Optional +RegistryEntries.Attributes[0].ValueExpression - string No Optional +RegistryEntries.Menus - Menus No Optional +RegistryEntries.Dependencies - Dependencies No Optional +Id BiosAttributeRegistry string Yes PASS +Description - string No Optional +Name Bios Attribute Registry string Yes PASS +Oem - Resource.Oem No Optional + +Change-Id: Iecc61018c350f0b8c89df59b2864b941508b1916 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: Snehalatha Venkatesh <snehalathax.v@intel.com> +--- + redfish-core/include/redfish.hpp | 3 + + .../include/registries/bios_registry.hpp | 41 ++ + redfish-core/lib/bios.hpp | 511 ++++++++++++++++++ + redfish-core/lib/message_registries.hpp | 11 +- + 4 files changed, 565 insertions(+), 1 deletion(-) + create mode 100644 redfish-core/include/registries/bios_registry.hpp + +diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp +index 0a97150..07a9417 100644 +--- a/redfish-core/include/redfish.hpp ++++ b/redfish-core/include/redfish.hpp +@@ -148,7 +148,10 @@ class RedfishService + requestRoutesSystemActionsReset(app); + requestRoutesSystemResetActionInfo(app); + requestRoutesBiosService(app); ++ requestRoutesBiosSettings(app); ++ requestRoutesBiosAttributeRegistry(app); + requestRoutesBiosReset(app); ++ requestRoutesBiosChangePassword(app); + + #ifdef BMCWEB_ENABLE_VM_NBDPROXY + requestNBDVirtualMediaRoutes(app); +diff --git a/redfish-core/include/registries/bios_registry.hpp b/redfish-core/include/registries/bios_registry.hpp +new file mode 100644 +index 0000000..c80937a +--- /dev/null ++++ b/redfish-core/include/registries/bios_registry.hpp +@@ -0,0 +1,41 @@ ++/* ++// Copyright (c) 2020 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++#pragma once ++#include <registries.hpp> ++ ++namespace redfish::message_registries::bios ++{ ++const Header header = { ++ "Copyright 2020 OpenBMC. All rights reserved.", ++ "#MessageRegistry.v1_4_0.MessageRegistry", ++ "BiosAttributeRegistry.1.0.0", ++ "Bios Attribute Registry", ++ "en", ++ "This registry defines the messages for bios attribute registry.", ++ "BiosAttributeRegistry", ++ "1.0.0", ++ "OpenBMC", ++}; ++// BiosAttributeRegistry registry is not defined in DMTF, We should use ++// OEM defined registries for this purpose. ++// Below link is wrong - We need to define OEM registries and use ++// appropriate data here. ++constexpr const char* url = ++ "https://redfish.dmtf.org/registries/BiosAttributeRegistry.1.0.0.json"; ++ ++constexpr std::array<MessageEntry, 0> registry = { ++}; ++} // namespace redfish::message_registries::bios +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index c2fb284..87536d6 100644 +--- a/redfish-core/lib/bios.hpp ++++ b/redfish-core/lib/bios.hpp +@@ -3,8 +3,140 @@ + #include <app.hpp> + #include <registries/privilege_registry.hpp> + #include <utils/fw_utils.hpp> ++ + namespace redfish + { ++ ++/*baseBIOSTable ++map{attributeName,struct{attributeType,readonlyStatus,displayname, ++ description,menuPath,current,default, ++ array{struct{optionstring,optionvalue}}}} ++*/ ++using BiosBaseTableType = std::vector<std::pair< ++ std::string, ++ std::tuple< ++ std::string, bool, std::string, std::string, std::string, ++ std::variant<int64_t, std::string>, std::variant<int64_t, std::string>, ++ std::vector< ++ std::tuple<std::string, std::variant<int64_t, std::string>>>>>>; ++using BiosBaseTableItemType = std::pair< ++ std::string, ++ std::tuple< ++ std::string, bool, std::string, std::string, std::string, ++ std::variant<int64_t, std::string>, std::variant<int64_t, std::string>, ++ std::vector< ++ std::tuple<std::string, std::variant<int64_t, std::string>>>>>; ++using OptionsItemType = ++ std::tuple<std::string, std::variant<int64_t, std::string>>; ++ ++enum BiosBaseTableIndex ++{ ++ biosBaseAttrType = 0, ++ biosBaseReadonlyStatus, ++ biosBaseDisplayName, ++ biosBaseDescription, ++ biosBaseMenuPath, ++ biosBaseCurrValue, ++ biosBaseDefaultValue, ++ biosBaseOptions ++}; ++enum OptionsItemIndex ++{ ++ optItemType = 0, ++ optItemValue ++}; ++/* ++ The Pending attribute name and new value. ++ ex- { {"QuietBoot",Type.Integer, 0x1}, ++ { "DdrFreqLimit",Type.String,"2933"} ++ } ++*/ ++using PendingAttributesType = std::vector<std::pair< ++ std::string, std::tuple<std::string, std::variant<int64_t, std::string>>>>; ++using PendingAttributesItemType = ++ std::pair<std::string, ++ std::tuple<std::string, std::variant<int64_t, std::string>>>; ++enum PendingAttributesIndex ++{ ++ pendingAttrType = 0, ++ pendingAttrValue ++}; ++static std::string mapAttrTypeToRedfish(const std::string_view typeDbus) ++{ ++ std::string ret; ++ if (typeDbus == "xyz.openbmc_project.BIOSConfig.Manager." ++ "AttributeType.Enumeration") ++ { ++ ret = "Enumeration"; ++ } ++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig." ++ "Manager.AttributeType.String") ++ { ++ ret = "String"; ++ } ++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig." ++ "Manager.AttributeType.Password") ++ { ++ ret = "Password"; ++ } ++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig." ++ "Manager.AttributeType.Integer") ++ { ++ ret = "Integer"; ++ } ++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig." ++ "Manager.AttributeType.Boolean") ++ { ++ ret = "Boolean"; ++ } ++ else ++ { ++ ret = "UNKNOWN"; ++ } ++ ++ return ret; ++} ++static std::string mapBoundTypeToRedfish(const std::string_view typeDbus) ++{ ++ std::string ret; ++ if (typeDbus == ++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.ScalarIncrement") ++ { ++ ret = "ScalarIncrement"; ++ } ++ else if (typeDbus == ++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.LowerBound") ++ { ++ ret = "LowerBound"; ++ } ++ else if (typeDbus == ++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.UpperBound") ++ { ++ ret = "UpperBound"; ++ } ++ else if (typeDbus == ++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.MinStringLength") ++ { ++ ret = "MinStringLength"; ++ } ++ else if (typeDbus == ++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.MaxStringLength") ++ { ++ ret = "MaxStringLength"; ++ } ++ else if (typeDbus == ++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.OneOf") ++ { ++ ret = "OneOf"; ++ } ++ else ++ { ++ ret = "UNKNOWN"; ++ } ++ ++ return ret; ++} ++ + /** + * BiosService class supports handle get method for bios. + */ +@@ -23,6 +155,85 @@ inline void + // Get the ActiveSoftwareImage and SoftwareImages + fw_util::populateFirmwareInformation(asyncResp, fw_util::biosPurpose, "", + true); ++ ++ asyncResp->res.jsonValue["@Redfish.Settings"] = { ++ {"@odata.type", "#Settings.v1_3_0.Settings"}, ++ {"SettingsObject", ++ {{"@odata.id", "/redfish/v1/Systems/system/Bios/Settings"}}}}; ++ asyncResp->res.jsonValue["AttributeRegistry"] = "BiosAttributeRegistry"; ++ asyncResp->res.jsonValue["Attributes"] = {}; ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec, ++ const GetObjectType& getObjectType) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " ++ << ec; ++ messages::internalError(asyncResp->res); ++ ++ return; ++ } ++ const std::string& service = getObjectType.begin()->first; ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp]( ++ const boost::system::error_code ec, ++ const std::variant<BiosBaseTableType>& retBiosTable) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "getBiosAttributes DBUS error: " ++ << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ const BiosBaseTableType* baseBiosTable = ++ std::get_if<BiosBaseTableType>(&retBiosTable); ++ nlohmann::json& attributesJson = ++ asyncResp->res.jsonValue["Attributes"]; ++ if (baseBiosTable == nullptr) ++ { ++ BMCWEB_LOG_ERROR << "baseBiosTable == nullptr "; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ for (const BiosBaseTableItemType& item : *baseBiosTable) ++ { ++ const std::string& key = item.first; ++ const std::string& itemType = ++ std::get<biosBaseAttrType>(item.second); ++ std::string attrType = mapAttrTypeToRedfish(itemType); ++ if (attrType == "String") ++ { ++ const std::string* currValue = ++ std::get_if<std::string>( ++ &std::get<biosBaseCurrValue>(item.second)); ++ attributesJson.emplace( ++ key, currValue != nullptr ? *currValue : ""); ++ } ++ else if (attrType == "Integer") ++ { ++ const int64_t* currValue = std::get_if<int64_t>( ++ &std::get<biosBaseCurrValue>(item.second)); ++ attributesJson.emplace( ++ key, currValue != nullptr ? *currValue : 0); ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "Unsupported attribute type."; ++ messages::internalError(asyncResp->res); ++ } ++ } ++ }, ++ service, "/xyz/openbmc_project/bios_config/manager", ++ "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.BIOSConfig.Manager", "BaseBIOSTable"); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject", ++ "/xyz/openbmc_project/bios_config/manager", ++ std::array<const char*, 0>()); + } + inline void requestRoutesBiosService(App& app) + { +@@ -31,6 +242,306 @@ inline void requestRoutesBiosService(App& app) + .methods(boost::beast::http::verb::get)(handleBiosServiceGet); + } + ++/** ++ * BiosSettings class supports handle GET/PATCH method for ++ * BIOS configuration pending settings. ++ */ ++inline void requestRoutesBiosSettings(App& app) ++{ ++ BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/Settings") ++ .privileges(redfish::privileges::getBios) ++ .methods(boost::beast::http::verb::get)( ++ [](const crow::Request&, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ asyncResp->res.jsonValue["@odata.id"] = ++ asyncResp->res.jsonValue["@odata.id"] = ++ "/redfish/v1/Systems/system/Bios/Settings"; ++ asyncResp->res.jsonValue["@odata.type"] = "#Bios.v1_1_0.Bios"; ++ asyncResp->res.jsonValue["Name"] = "Bios Settings Version 1"; ++ asyncResp->res.jsonValue["Id"] = "BiosSettingsV1"; ++ asyncResp->res.jsonValue["AttributeRegistry"] = ++ "BiosAttributeRegistry"; ++ asyncResp->res.jsonValue["Attributes"] = {}; ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec, ++ const GetObjectType& getObjectType) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "ObjectMapper::GetObject call failed: " ++ << ec; ++ messages::internalError(asyncResp->res); ++ ++ return; ++ } ++ std::string service = getObjectType.begin()->first; ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp]( ++ const boost::system::error_code ec, ++ const std::variant<PendingAttributesType>& ++ retPendingAttributes) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "getBiosSettings DBUS error: " << ec; ++ messages::resourceNotFound( ++ asyncResp->res, "Systems/system/Bios", ++ "Settings"); ++ return; ++ } ++ const PendingAttributesType* pendingAttributes = ++ std::get_if<PendingAttributesType>( ++ &retPendingAttributes); ++ nlohmann::json& attributesJson = ++ asyncResp->res.jsonValue["Attributes"]; ++ if (pendingAttributes == nullptr) ++ { ++ BMCWEB_LOG_ERROR ++ << "pendingAttributes == nullptr "; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ for (const PendingAttributesItemType& item : ++ *pendingAttributes) ++ { ++ const std::string& key = item.first; ++ const std::string& itemType = ++ std::get<pendingAttrType>(item.second); ++ std::string attrType = ++ mapAttrTypeToRedfish(itemType); ++ if (attrType == "String") ++ { ++ const std::string* currValue = ++ std::get_if<std::string>( ++ &std::get<pendingAttrValue>( ++ item.second)); ++ attributesJson.emplace( ++ key, currValue != nullptr ++ ? *currValue ++ : ""); ++ } ++ else if (attrType == "Integer") ++ { ++ const int64_t* currValue = ++ std::get_if<int64_t>( ++ &std::get<pendingAttrValue>( ++ item.second)); ++ attributesJson.emplace( ++ key, currValue != nullptr ++ ? *currValue ++ : 0); ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR ++ << "Unsupported attribute type."; ++ messages::internalError(asyncResp->res); ++ } ++ } ++ }, ++ service, "/xyz/openbmc_project/bios_config/manager", ++ "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.BIOSConfig.Manager", ++ "PendingAttributes"); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject", ++ "/xyz/openbmc_project/bios_config/manager", ++ std::array<const char*, 0>()); ++ }); ++} ++/** ++ * BiosAttributeRegistry class supports handle get method for BIOS attribute ++ * registry. ++ */ ++inline void requestRoutesBiosAttributeRegistry(App& app) ++{ ++ BMCWEB_ROUTE( ++ app, ++ "/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry/") ++ .privileges(redfish::privileges::getBios) ++ .methods( ++ boost::beast::http::verb:: ++ get)([](const crow::Request&, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ asyncResp->res.jsonValue["@odata.id"] = ++ "/redfish/v1/Registries/BiosAttributeRegistry/" ++ "BiosAttributeRegistry"; ++ asyncResp->res.jsonValue["@odata.type"] = ++ "#AttributeRegistry.v1_3_2.AttributeRegistry"; ++ asyncResp->res.jsonValue["Name"] = "Bios Attribute Registry"; ++ asyncResp->res.jsonValue["Id"] = "BiosAttributeRegistry"; ++ asyncResp->res.jsonValue["RegistryVersion"] = "1.0.0"; ++ asyncResp->res.jsonValue["Language"] = "en"; ++ asyncResp->res.jsonValue["OwningEntity"] = "OpenBMC"; ++ asyncResp->res.jsonValue["RegistryEntries"]["Attributes"] = ++ nlohmann::json::array(); ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec, ++ const GetObjectType& getObjectType) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "ObjectMapper::GetObject call failed: " << ec; ++ messages::internalError(asyncResp->res); ++ ++ return; ++ } ++ std::string service = getObjectType.begin()->first; ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec, ++ const std::variant<BiosBaseTableType>& ++ retBiosTable) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "getBiosAttributeRegistry DBUS error: " ++ << ec; ++ messages::resourceNotFound( ++ asyncResp->res, "Registries/Bios", "Bios"); ++ return; ++ } ++ const BiosBaseTableType* baseBiosTable = ++ std::get_if<BiosBaseTableType>(&retBiosTable); ++ nlohmann::json& attributeArray = ++ asyncResp->res ++ .jsonValue["RegistryEntries"]["Attributes"]; ++ nlohmann::json optionsArray = ++ nlohmann::json::array(); ++ if (baseBiosTable == nullptr) ++ { ++ BMCWEB_LOG_ERROR << "baseBiosTable == nullptr "; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ for (const BiosBaseTableItemType& item : ++ *baseBiosTable) ++ { ++ const std::string& itemType = ++ std::get<biosBaseAttrType>(item.second); ++ std::string attrType = ++ mapAttrTypeToRedfish(itemType); ++ if (attrType == "UNKNOWN") ++ { ++ BMCWEB_LOG_ERROR << "attrType == UNKNOWN"; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ nlohmann::json attributeItem; ++ attributeItem["AttributeName"] = item.first; ++ attributeItem["Type"] = attrType; ++ attributeItem["ReadOnly"] = ++ std::get<biosBaseReadonlyStatus>( ++ item.second); ++ attributeItem["DisplayName"] = ++ std::get<biosBaseDisplayName>(item.second); ++ attributeItem["HelpText"] = ++ std::get<biosBaseDescription>(item.second); ++ attributeItem["MenuPath"] = ++ std::get<biosBaseMenuPath>(item.second); ++ ++ if (attrType == "String") ++ { ++ const std::string* currValue = ++ std::get_if<std::string>( ++ &std::get<biosBaseCurrValue>( ++ item.second)); ++ const std::string* defValue = ++ std::get_if<std::string>( ++ &std::get<biosBaseDefaultValue>( ++ item.second)); ++ attributeItem["CurrentValue"] = ++ currValue != nullptr ? *currValue : ""; ++ attributeItem["DefaultValue"] = ++ defValue != nullptr ? *defValue : ""; ++ } ++ else if (attrType == "Integer") ++ { ++ const int64_t* currValue = ++ std::get_if<int64_t>( ++ &std::get<biosBaseCurrValue>( ++ item.second)); ++ const int64_t* defValue = ++ std::get_if<int64_t>( ++ &std::get<biosBaseDefaultValue>( ++ item.second)); ++ attributeItem["CurrentValue"] = ++ currValue != nullptr ? *currValue : 0; ++ attributeItem["DefaultValue"] = ++ defValue != nullptr ? *defValue : 0; ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR ++ << "Unsupported attribute type."; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ const std::vector<OptionsItemType>& ++ optionsVector = ++ std::get<biosBaseOptions>(item.second); ++ for (const OptionsItemType& optItem : ++ optionsVector) ++ { ++ nlohmann::json optItemJson; ++ const std::string& strOptItemType = ++ std::get<optItemType>(optItem); ++ std::string optItemTypeRedfish = ++ mapBoundTypeToRedfish(strOptItemType); ++ if (optItemTypeRedfish == "UNKNOWN") ++ { ++ BMCWEB_LOG_ERROR ++ << "optItemTypeRedfish == UNKNOWN"; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ if (optItemTypeRedfish == "OneOf") ++ { ++ const std::string* currValue = ++ std::get_if<std::string>( ++ &std::get<optItemValue>( ++ optItem)); ++ optItemJson[optItemTypeRedfish] = ++ currValue != nullptr ? *currValue ++ : ""; ++ } ++ else ++ { ++ const int64_t* currValue = ++ std::get_if<int64_t>( ++ &std::get<optItemValue>( ++ optItem)); ++ optItemJson[optItemTypeRedfish] = ++ currValue != nullptr ? *currValue ++ : 0; ++ } ++ ++ optionsArray.push_back(optItemJson); ++ } ++ ++ attributeItem["Value"] = optionsArray; ++ attributeArray.push_back(attributeItem); ++ } ++ }, ++ service, "/xyz/openbmc_project/bios_config/manager", ++ "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.BIOSConfig.Manager", ++ "BaseBIOSTable"); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject", ++ "/xyz/openbmc_project/bios_config/manager", ++ std::array<const char*, 0>()); ++ }); ++} ++ + /** + * BiosReset class supports handle POST method for Reset bios. + * The class retrieves and sends data directly to D-Bus. +diff --git a/redfish-core/lib/message_registries.hpp b/redfish-core/lib/message_registries.hpp +index 43359e3..b2fb43c 100644 +--- a/redfish-core/lib/message_registries.hpp ++++ b/redfish-core/lib/message_registries.hpp +@@ -17,6 +17,7 @@ + + #include "registries.hpp" + #include "registries/base_message_registry.hpp" ++#include "registries/bios_registry.hpp" + #include "registries/openbmc_message_registry.hpp" + #include "registries/resource_event_message_registry.hpp" + #include "registries/task_event_message_registry.hpp" +@@ -125,7 +126,6 @@ inline void requestRoutesMessageRegistryFile(App& app) + inline void handleMessageRegistryGet( + const crow::Request&, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& registry, const std::string& registryMatch) +- + { + const message_registries::Header* header; + std::vector<const message_registries::MessageEntry*> registryEntries; +@@ -165,6 +165,15 @@ inline void handleMessageRegistryGet( + registryEntries.emplace_back(&entry); + } + } ++ else if (registry == "BiosAttributeRegistry") ++ { ++ header = &message_registries::bios::header; ++ for (const message_registries::MessageEntry& entry : ++ message_registries::bios::registry) ++ { ++ registryEntries.emplace_back(&entry); ++ } ++ } + else + { + messages::resourceNotFound( +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0002-BaseBiosTable-Add-support-for-PATCH-operation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0002-BaseBiosTable-Add-support-for-PATCH-operation.patch new file mode 100644 index 000000000..099e6ddf5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0002-BaseBiosTable-Add-support-for-PATCH-operation.patch @@ -0,0 +1,148 @@ +From ffa924ef204930a5bb442bf654eac02543acfb8f Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Wed, 30 Jun 2021 15:18:46 +0000 +Subject: [PATCH 2/5] BaseBiosTable: Add support for PATCH operation + +This commit brings in support for PATCH operation of the +bios variables that updates the BaseBiosTable. + +Tested-By: +* Passed Redfish validator + +* Single Attribute: +PATCH https://${bmc}/redfish/v1/Systems/system/Bios/Settings -d +'{"data":[{"AttributeName": <attribute name>, "AttributeType": +<attribute type>, "AttributeValue": <attribute value>}]}' + +* Multiple Attributes: +PATCH https://${bmc}/redfish/v1/Systems/system/Bios/Settings -d +'{"data":[{"AttributeName": <attribute name>, "AttributeType": +<attribute type>, "AttributeValue": <attribute value>}, +{"AttributeName": <attribute name>, "AttributeType": +<attribute type>, "AttributeValue": <attribute value>}]}' + +This makes use of the "Set" of "PendingAttributes" in the +backend and that updates the BaseBiosTable. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Change-Id: I12e78e5ac623c264c7a3e1dd5198aca67172736d +--- + redfish-core/lib/bios.hpp | 95 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 95 insertions(+) + +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index c1a5c56..14d2171 100644 +--- a/redfish-core/lib/bios.hpp ++++ b/redfish-core/lib/bios.hpp +@@ -96,6 +96,29 @@ static std::string mapAttrTypeToRedfish(const std::string_view typeDbus) + + return ret; + } ++static std::string mapRedfishToAttrType(const std::string_view type) ++{ ++ std::string ret; ++ if (type == "string") ++ { ++ ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String"; ++ } ++ else if (type == "int") ++ { ++ ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Integer"; ++ } ++ else if (type == "enum") ++ { ++ ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType." ++ "Enumeration"; ++ } ++ else ++ { ++ ret = "UNKNOWN"; ++ } ++ ++ return ret; ++} + static std::string mapBoundTypeToRedfish(const std::string_view typeDbus) + { + std::string ret; +@@ -370,6 +393,78 @@ inline void requestRoutesBiosSettings(App& app) + "/xyz/openbmc_project/bios_config/manager", + std::array<const char*, 0>()); + }); ++ ++ BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/Settings") ++ .privileges({{"ConfigureComponents"}}) ++ .methods(boost::beast::http::verb::patch)( ++ [](const crow::Request& req, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ nlohmann::json inpJson; ++ ++ if (!redfish::json_util::readJson(req, asyncResp->res, "data", ++ inpJson)) ++ { ++ return; ++ } ++ ++ for (auto& attrInfo : inpJson) ++ { ++ std::optional<std::string> attrName; ++ std::optional<std::string> attrType; ++ std::optional<std::string> attrValue; ++ if (!json_util::getValueFromJsonObject( ++ attrInfo, "AttributeName", attrName)) ++ { ++ messages::propertyMissing(asyncResp->res, ++ "AttributeName"); ++ return; ++ } ++ if (!json_util::getValueFromJsonObject( ++ attrInfo, "AttributeType", attrType)) ++ { ++ messages::propertyMissing(asyncResp->res, ++ "AttributeType"); ++ return; ++ } ++ if (!json_util::getValueFromJsonObject( ++ attrInfo, "AttributeValue", attrValue)) ++ { ++ messages::propertyMissing(asyncResp->res, ++ "AttributeValue"); ++ return; ++ } ++ std::string biosAttrType = mapRedfishToAttrType(*attrType); ++ ++ if (biosAttrType == "UNKNOWN") ++ { ++ BMCWEB_LOG_ERROR << "Invalid attribute type"; ++ messages::propertyValueNotInList( ++ asyncResp->res, "AttributeType", *attrType); ++ return; ++ } ++ ++ PendingAttributesType pendingAttributes; ++ pendingAttributes.emplace_back(std::make_pair( ++ *attrName, std::make_tuple(biosAttrType, *attrValue))); ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "doPatch resp_handler got error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ }, ++ "xyz.openbmc_project.BIOSConfigManager", ++ "/xyz/openbmc_project/bios_config/manager", ++ "org.freedesktop.DBus.Properties", "Set", ++ "xyz.openbmc_project.BIOSConfig.Manager", ++ "PendingAttributes", ++ std::variant<PendingAttributesType>(pendingAttributes)); ++ } ++ }); + } + /** + * BiosAttributeRegistry class supports handle get method for BIOS attribute +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0003-Add-support-to-ResetBios-action.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0003-Add-support-to-ResetBios-action.patch new file mode 100644 index 000000000..5ed92cc3e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0003-Add-support-to-ResetBios-action.patch @@ -0,0 +1,53 @@ +From b7adca60dd69ac9566dc8f417065e244198fc711 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Wed, 6 Oct 2021 22:27:20 +0000 +Subject: [PATCH] Add support to ResetBios action + +Tested: + +Bios reset flag can be modified throw redfish +POST https://IP_ADDR/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios + +Change-Id: Ic719c55705e5f634539b3dd858b60922e505a8d0 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> + +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +--- + redfish-core/lib/bios.hpp | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index f5aa7b7..f613613 100644 +--- a/redfish-core/lib/bios.hpp ++++ b/redfish-core/lib/bios.hpp +@@ -648,17 +648,23 @@ inline void + handleBiosResetPost(const crow::Request&, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) + { ++ std::string resetFlag = ++ "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag.FactoryDefaults"; ++ + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec) { + if (ec) + { +- BMCWEB_LOG_ERROR << "Failed to reset bios: " << ec; ++ BMCWEB_LOG_ERROR << "doPost bios reset got error " << ec; + messages::internalError(asyncResp->res); + return; + } + }, +- "org.open_power.Software.Host.Updater", "/xyz/openbmc_project/software", +- "xyz.openbmc_project.Common.FactoryReset", "Reset"); ++ "xyz.openbmc_project.BIOSConfigManager", ++ "/xyz/openbmc_project/bios_config/manager", ++ "org.freedesktop.DBus.Properties", "Set", ++ "xyz.openbmc_project.BIOSConfig.Manager", "ResetBIOSSettings", ++ std::variant<std::string>(resetFlag)); + } + + inline void requestRoutesBiosReset(App& app) +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0004-Add-support-to-ChangePassword-action.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0004-Add-support-to-ChangePassword-action.patch new file mode 100644 index 000000000..4bfca3006 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0004-Add-support-to-ChangePassword-action.patch @@ -0,0 +1,117 @@ +From 22956921a228f6f1cbbbd3045a3cc3969732dca3 Mon Sep 17 00:00:00 2001 +From: Arun Lal K M <arun.lal@intel.com> +Date: Fri, 8 Oct 2021 20:56:00 +0000 +Subject: [PATCH] Add support to ChangePassword action + +Tested: + +Passed Redfish validator. +Bios change password: +root@intel-obmc:~# cat /var/lib/bios-settings-manager/seedData +{ +"UserPwdHash": "08D91157785366CDC3AA64D87E5E3C621EDAB13E26B6E484397EBA5E459E54C567BF5B1FFB36A43B6142B18F8D642E9D", +"AdminPwdHash": "08D91157785366CDC3AA64D87E5E3C621EDAB13E26B6E484397EBA5E459E54C567BF5B1FFB36A43B6142B18F8D642E9D", +"Seed": "123456", +"HashAlgo": "SHA384" +} +POST https://IP_ADDR/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword +{ + "NewPassword": "12345678", + "OldPassword": "1234567890", + "PasswordName": "Administrator" +} +root@intel-obmc:~# cat /var/lib/bios-settings-manager/passwordData +{ + "CurrentPassword": "1234567890", + "IsAdminPwdChanged": 1, + "IsUserPwdChanged": 0, + "NewPassword": "2DD65D57EB60B1D92C5F3D2DC84724FCEE7BC02E57AA75E834712266ED94CAC704047B2FF7CEC1C36BED280B36BB5AC6", + "UserName": "Administrator" +} + +Signed-off-by: Arun Lal K M <arun.lal@intel.com> +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + redfish-core/lib/bios.hpp | 59 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 59 insertions(+) + +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index f613613..b06a904 100644 +--- a/redfish-core/lib/bios.hpp ++++ b/redfish-core/lib/bios.hpp +@@ -175,6 +175,10 @@ inline void + asyncResp->res.jsonValue["Actions"]["#Bios.ResetBios"] = { + {"target", "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios"}}; + ++ asyncResp->res.jsonValue["Actions"]["#Bios.ChangePassword"] = { ++ {"target", "/redfish/v1/Systems/system/Bios/Actions/" ++ "Bios.ChangePassword"}}; ++ + // Get the ActiveSoftwareImage and SoftwareImages + fw_util::populateFirmwareInformation(asyncResp, fw_util::biosPurpose, "", + true); +@@ -265,6 +269,61 @@ inline void requestRoutesBiosService(App& app) + .methods(boost::beast::http::verb::get)(handleBiosServiceGet); + } + ++/** ++ * BiosChangePassword class supports handle POST method for change bios ++ * password. The class retrieves and sends data directly to D-Bus. ++ */ ++inline void requestRoutesBiosChangePassword(App& app) ++{ ++ BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/") ++ .privileges({{"ConfigureComponents"}}) ++ .methods(boost::beast::http::verb::post)( ++ [](const crow::Request& req, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ std::string currentPassword, newPassword, userName; ++ if (!json_util::readJson(req, asyncResp->res, "NewPassword", ++ newPassword, "OldPassword", ++ currentPassword, "PasswordName", ++ userName)) ++ { ++ return; ++ } ++ if (currentPassword.empty()) ++ { ++ messages::actionParameterUnknown( ++ asyncResp->res, "ChangePassword", "OldPassword"); ++ return; ++ } ++ if (newPassword.empty()) ++ { ++ messages::actionParameterUnknown( ++ asyncResp->res, "ChangePassword", "NewPassword"); ++ return; ++ } ++ if (userName.empty()) ++ { ++ messages::actionParameterUnknown( ++ asyncResp->res, "ChangePassword", "PasswordName"); ++ return; ++ } ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec) { ++ if (ec) ++ { ++ BMCWEB_LOG_CRITICAL ++ << "Failed in doPost(BiosChangePassword) " ++ << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ }, ++ "xyz.openbmc_project.BIOSConfigPassword", ++ "/xyz/openbmc_project/bios_config/password", ++ "xyz.openbmc_project.BIOSConfig.Password", "ChangePassword", ++ userName, currentPassword, newPassword); ++ }); ++} ++ + /** + * BiosSettings class supports handle GET/PATCH method for + * BIOS configuration pending settings. +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0005-Fix-remove-bios-user-pwd-change-option-via-Redfish.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0005-Fix-remove-bios-user-pwd-change-option-via-Redfish.patch new file mode 100644 index 000000000..26393bfee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0005-Fix-remove-bios-user-pwd-change-option-via-Redfish.patch @@ -0,0 +1,46 @@ +From edc6925e8c0d9f60da1f70c524261efaf05b2710 Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Wed, 30 Jun 2021 15:42:06 +0000 +Subject: [PATCH 5/5] Fix:remove bios user pwd change option via Redfish + +BMC should not provide user bios setup password change option via +Redfish as per bios security requirements. Only Admin BIOS setup +password is supported. + +Added check for the password name action parameter and +do not allow if it has User Password value from redfish side. + +Tested: sent POST query in redfish on URI: +https://<ip>/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword +error occurs for UserPassword parameter and allows for AdminPassword. + +Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com> +Change-Id: I169cc6a4f786625d9e8b8dfe56816d52b1740f4c +--- + redfish-core/lib/bios.hpp | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index 0250c59..360a749 100644 +--- a/redfish-core/lib/bios.hpp ++++ b/redfish-core/lib/bios.hpp +@@ -323,6 +323,16 @@ inline void requestRoutesBiosChangePassword(App& app) + asyncResp->res, "ChangePassword", "PasswordName"); + return; + } ++ ++ // In Intel BIOS, we are not supporting user password in BIOS ++ // setup ++ if (userName == "UserPassword") ++ { ++ messages::actionParameterUnknown( ++ asyncResp->res, "ChangePassword", "PasswordName"); ++ return; ++ } ++ + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec) { + if (ec) +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0006-Add-fix-for-broken-feature-Pending-Attributes.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0006-Add-fix-for-broken-feature-Pending-Attributes.patch new file mode 100644 index 000000000..2e72a639d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0006-Add-fix-for-broken-feature-Pending-Attributes.patch @@ -0,0 +1,928 @@ +From f75beb0472a42d5af512661470aadf12ac460470 Mon Sep 17 00:00:00 2001 +From: Arun Lal K M <arun.lal@intel.com> +Date: Fri, 8 Oct 2021 21:30:33 +0000 +Subject: [PATCH] Add fix for broken feature 'Pending Attributes'. + +Fix is added for the following: +1) GET to 'redfish/v1/Systems/system/Bios'. +2) PATCH to 'redfish/v1/Systems/system/Bios/Settings'. +3) GET to 'redfish/v1/Systems/system/Bios/Settings'. +4) Fix for incremental duplicate values in BiosAttributeRegistry. +5) POST to '/redfish/v1/Systems/system/Bios/Actions + /Bios.ChangePassword/'. + +Tested: +By giving PATCH to 'redfish/v1/Systems/system/Bios/Settings' +PATCH command raw data: +{ + "data":{ + "AmpPrefetchEnable": "0x1", + "Ce2LmLoggingEn": "0x1", + "DfxEadrDebugLogs": "0x2", + "PsfUrEnable": "0x1", + "ATS": "0x0" + } +} + +Response: +{ + "@Message.ExtendedInfo": [ + { + "@odata.type": "#Message.v1_1_1.Message", + "Message": "Successfully Completed Request", + "MessageArgs": [], + "MessageId": "Base.1.8.1.Success", + "MessageSeverity": "OK", + "Resolution": "None" + } + ] +} + +By giving GET to 'redfish/v1/Systems/system/Bios' +Response: +{ + "@Redfish.Settings": { + "@odata.type": "#Settings.v1_3_0.Settings", + "SettingsObject": { + "@odata.id": + "/redfish/v1/Systems/system/Bios/Settings" + } + }, + "@odata.id": "/redfish/v1/Systems/system/Bios", + "@odata.type": "#Bios.v1_1_0.Bios", + "Actions": { + "#Bios.ChangePassword": { + "target": + "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword" + }, + "#Bios.ResetBios": { + "target": + "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios" + } + }, + "AttributeRegistry": "BiosAttributeRegistry", + "Attributes": { + "AEPErrorInjEn": "0x00", + "ARIEnable": "0x01", + "ARIForward": "0x00", + ... + ... + ... + "txEqCalibration": "0x01", + "volMemMode": "0x00", + "wrVrefCenter": "0x01" + }, + "Description": "BIOS Configuration Service", + "Id": "BIOS", + "Links": { + "ActiveSoftwareImage": { + "@odata.id": + "/redfish/v1/UpdateService/FirmwareInventory/bios_active" + }, + "SoftwareImages": [ + { + "@odata.id": + "/redfish/v1/UpdateService/FirmwareInventory/bios_active" + } + ], + "SoftwareImages@odata.count": 1 + }, + "Name": "BIOS Configuration" +} + +By giving GET to 'redfish/v1/Systems/system/Bios/Settings' +Response: +{ + "@odata.id": "/redfish/v1/Systems/system/Bios/Settings", + "@odata.type": "#Bios.v1_1_0.Bios", + "AttributeRegistry": "BiosAttributeRegistry", + "Attributes": { + "ATS": "0x0", + "AmpPrefetchEnable": "0x1", + "Ce2LmLoggingEn": "0x1", + "DfxEadrDebugLogs": "0x2", + "PsfUrEnable": "0x1" + }, + "Id": "BiosSettingsV1", + "Name": "Bios Settings Version 1" +} + +By giving POST to '/redfish/v1/Systems/system/Bios/Actions +/Bios.ChangePassword/' +Response: Success + +By running Redfish-Service-Validator +Result: +Elapsed time: 0:09:36 +invalidPropertyValue: 108 +metadataNamespaces: 2185 +missingNamespaces: 1 +optionalAction: 9 +pass: 13772 +passAction: 22 +passGet: 541 +reflink: 1 +repeat: 47 +serviceNamespaces: 75 +skipOptional: 9276 +unverifiedComplexAdditional: 1 +warnDeprecated: 230 +warningPresent: 54 +Validation has succeeded. + +Signed-off-by: Arun Lal K M <arun.lal@intel.com> +Signed-off-by: Snehalatha Venkatesh <snehalathax.v@intel.com> +--- + redfish-core/lib/bios.hpp | 586 ++++++++++++++++++++++++-------------- + 1 file changed, 376 insertions(+), 210 deletions(-) + +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index 0bb0b9e..de79be2 100644 +--- a/redfish-core/lib/bios.hpp ++++ b/redfish-core/lib/bios.hpp +@@ -12,13 +12,15 @@ map{attributeName,struct{attributeType,readonlyStatus,displayname, + description,menuPath,current,default, + array{struct{optionstring,optionvalue}}}} + */ +-using BiosBaseTableType = std::vector<std::pair< ++ ++using BiosBaseTableType = boost::container::flat_map< + std::string, + std::tuple< + std::string, bool, std::string, std::string, std::string, + std::variant<int64_t, std::string>, std::variant<int64_t, std::string>, + std::vector< +- std::tuple<std::string, std::variant<int64_t, std::string>>>>>>; ++ std::tuple<std::string, std::variant<int64_t, std::string>>>>>; ++ + using BiosBaseTableItemType = std::pair< + std::string, + std::tuple< +@@ -29,6 +31,13 @@ using BiosBaseTableItemType = std::pair< + using OptionsItemType = + std::tuple<std::string, std::variant<int64_t, std::string>>; + ++using PendingAttributesType = boost::container::flat_map< ++ std::string, std::tuple<std::string, std::variant<int64_t, std::string>>>; ++ ++using PendingAttributesItemType = ++ std::pair<std::string, ++ std::tuple<std::string, std::variant<int64_t, std::string>>>; ++ + enum BiosBaseTableIndex + { + biosBaseAttrType = 0, +@@ -45,17 +54,7 @@ enum OptionsItemIndex + optItemType = 0, + optItemValue + }; +-/* +- The Pending attribute name and new value. +- ex- { {"QuietBoot",Type.Integer, 0x1}, +- { "DdrFreqLimit",Type.String,"2933"} +- } +-*/ +-using PendingAttributesType = std::vector<std::pair< +- std::string, std::tuple<std::string, std::variant<int64_t, std::string>>>>; +-using PendingAttributesItemType = +- std::pair<std::string, +- std::tuple<std::string, std::variant<int64_t, std::string>>>; ++ + enum PendingAttributesIndex + { + pendingAttrType = 0, +@@ -64,31 +63,16 @@ enum PendingAttributesIndex + static std::string mapAttrTypeToRedfish(const std::string_view typeDbus) + { + std::string ret; +- if (typeDbus == "xyz.openbmc_project.BIOSConfig.Manager." +- "AttributeType.Enumeration") +- { +- ret = "Enumeration"; +- } +- else if (typeDbus == "xyz.openbmc_project.BIOSConfig." +- "Manager.AttributeType.String") ++ if (typeDbus == "xyz.openbmc_project.BIOSConfig." ++ "Manager.AttributeType.String") + { + ret = "String"; + } +- else if (typeDbus == "xyz.openbmc_project.BIOSConfig." +- "Manager.AttributeType.Password") +- { +- ret = "Password"; +- } + else if (typeDbus == "xyz.openbmc_project.BIOSConfig." + "Manager.AttributeType.Integer") + { + ret = "Integer"; + } +- else if (typeDbus == "xyz.openbmc_project.BIOSConfig." +- "Manager.AttributeType.Boolean") +- { +- ret = "Boolean"; +- } + else + { + ret = "UNKNOWN"; +@@ -96,29 +80,7 @@ static std::string mapAttrTypeToRedfish(const std::string_view typeDbus) + + return ret; + } +-static std::string mapRedfishToAttrType(const std::string_view type) +-{ +- std::string ret; +- if (type == "string") +- { +- ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String"; +- } +- else if (type == "int") +- { +- ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Integer"; +- } +- else if (type == "enum") +- { +- ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType." +- "Enumeration"; +- } +- else +- { +- ret = "UNKNOWN"; +- } + +- return ret; +-} + static std::string mapBoundTypeToRedfish(const std::string_view typeDbus) + { + std::string ret; +@@ -201,6 +163,15 @@ inline void + + return; + } ++ ++ if (getObjectType.empty()) ++ { ++ BMCWEB_LOG_ERROR << "getObjectType is empty."; ++ messages::internalError(asyncResp->res); ++ ++ return; ++ } ++ + const std::string& service = getObjectType.begin()->first; + + crow::connections::systemBus->async_method_call( +@@ -220,7 +191,7 @@ inline void + asyncResp->res.jsonValue["Attributes"]; + if (baseBiosTable == nullptr) + { +- BMCWEB_LOG_ERROR << "baseBiosTable == nullptr "; ++ BMCWEB_LOG_ERROR << "baseBiosTable is empty"; + messages::internalError(asyncResp->res); + return; + } +@@ -248,7 +219,6 @@ inline void + else + { + BMCWEB_LOG_ERROR << "Unsupported attribute type."; +- messages::internalError(asyncResp->res); + } + } + }, +@@ -275,8 +245,9 @@ inline void requestRoutesBiosService(App& app) + */ + inline void requestRoutesBiosChangePassword(App& app) + { +- BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/") +- .privileges({{"ConfigureComponents"}}) ++ BMCWEB_ROUTE(app, ++ "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword/") ++ .privileges(redfish::privileges::postBios) + .methods(boost::beast::http::verb::post)( + [](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { +@@ -342,180 +313,298 @@ inline void requestRoutesBiosSettings(App& app) + { + BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/Settings") + .privileges(redfish::privileges::getBios) +- .methods(boost::beast::http::verb::get)( +- [](const crow::Request&, +- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { +- asyncResp->res.jsonValue["@odata.id"] = +- asyncResp->res.jsonValue["@odata.id"] = +- "/redfish/v1/Systems/system/Bios/Settings"; +- asyncResp->res.jsonValue["@odata.type"] = "#Bios.v1_1_0.Bios"; +- asyncResp->res.jsonValue["Name"] = "Bios Settings Version 1"; +- asyncResp->res.jsonValue["Id"] = "BiosSettingsV1"; +- asyncResp->res.jsonValue["AttributeRegistry"] = +- "BiosAttributeRegistry"; +- asyncResp->res.jsonValue["Attributes"] = {}; ++ .methods( ++ boost::beast::http::verb:: ++ get)([](const crow::Request&, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ asyncResp->res.jsonValue["@odata.id"] = ++ "/redfish/v1/Systems/system/Bios/Settings"; ++ asyncResp->res.jsonValue["@odata.type"] = "#Bios.v1_1_0.Bios"; ++ asyncResp->res.jsonValue["Name"] = "Bios Settings Version 1"; ++ asyncResp->res.jsonValue["Id"] = "BiosSettingsV1"; ++ asyncResp->res.jsonValue["AttributeRegistry"] = ++ "BiosAttributeRegistry"; ++ asyncResp->res.jsonValue["Attributes"] = {}; + +- crow::connections::systemBus->async_method_call( +- [asyncResp](const boost::system::error_code ec, +- const GetObjectType& getObjectType) { +- if (ec) +- { +- BMCWEB_LOG_ERROR +- << "ObjectMapper::GetObject call failed: " +- << ec; +- messages::internalError(asyncResp->res); ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec, ++ const GetObjectType& getObjectType) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "ObjectMapper::GetObject call failed: " << ec; ++ messages::internalError(asyncResp->res); + +- return; +- } +- std::string service = getObjectType.begin()->first; +- +- crow::connections::systemBus->async_method_call( +- [asyncResp]( +- const boost::system::error_code ec, +- const std::variant<PendingAttributesType>& +- retPendingAttributes) { +- if (ec) +- { +- BMCWEB_LOG_ERROR +- << "getBiosSettings DBUS error: " << ec; +- messages::resourceNotFound( +- asyncResp->res, "Systems/system/Bios", +- "Settings"); +- return; +- } +- const PendingAttributesType* pendingAttributes = +- std::get_if<PendingAttributesType>( +- &retPendingAttributes); +- nlohmann::json& attributesJson = +- asyncResp->res.jsonValue["Attributes"]; +- if (pendingAttributes == nullptr) +- { +- BMCWEB_LOG_ERROR +- << "pendingAttributes == nullptr "; +- messages::internalError(asyncResp->res); +- return; +- } +- for (const PendingAttributesItemType& item : +- *pendingAttributes) ++ return; ++ } ++ ++ if (getObjectType.empty()) ++ { ++ BMCWEB_LOG_ERROR << "getObjectType is empty."; ++ messages::internalError(asyncResp->res); ++ ++ return; ++ } ++ ++ std::string service = getObjectType.begin()->first; ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp](const boost::system::error_code ec, ++ const std::variant<PendingAttributesType>& ++ retPendingAttributes) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "getBiosSettings DBUS error: " << ec; ++ messages::resourceNotFound( ++ asyncResp->res, "Systems/system/Bios", ++ "Settings"); ++ return; ++ } ++ ++ const PendingAttributesType* pendingAttributes = ++ std::get_if<PendingAttributesType>( ++ &retPendingAttributes); ++ nlohmann::json& attributesJson = ++ asyncResp->res.jsonValue["Attributes"]; ++ if (pendingAttributes == nullptr) ++ { ++ BMCWEB_LOG_ERROR ++ << "pendingAttributes is empty"; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ for (const PendingAttributesItemType& ++ pendingAttributesItem : *pendingAttributes) ++ { ++ const std::string& biosAttrType = ++ std::get<pendingAttrType>( ++ pendingAttributesItem.second); ++ ++ std::string itemType = ++ mapAttrTypeToRedfish(biosAttrType); ++ ++ if (itemType == "String") + { +- const std::string& key = item.first; +- const std::string& itemType = +- std::get<pendingAttrType>(item.second); +- std::string attrType = +- mapAttrTypeToRedfish(itemType); +- if (attrType == "String") +- { +- const std::string* currValue = +- std::get_if<std::string>( +- &std::get<pendingAttrValue>( +- item.second)); +- attributesJson.emplace( +- key, currValue != nullptr +- ? *currValue +- : ""); +- } +- else if (attrType == "Integer") ++ const std::string* currValue = ++ std::get_if<std::string>( ++ &std::get<pendingAttrValue>( ++ pendingAttributesItem.second)); ++ ++ if (!currValue) + { +- const int64_t* currValue = +- std::get_if<int64_t>( +- &std::get<pendingAttrValue>( +- item.second)); +- attributesJson.emplace( +- key, currValue != nullptr +- ? *currValue +- : 0); ++ BMCWEB_LOG_ERROR ++ << "No string data in pending " ++ "attributes item data"; ++ messages::internalError(asyncResp->res); ++ return; + } +- else ++ ++ attributesJson.emplace( ++ pendingAttributesItem.first, ++ *currValue); ++ } ++ else if (itemType == "Integer") ++ { ++ const int64_t* currValue = ++ std::get_if<int64_t>( ++ &std::get<pendingAttrValue>( ++ pendingAttributesItem.second)); ++ ++ if (!currValue) + { + BMCWEB_LOG_ERROR +- << "Unsupported attribute type."; ++ << "No int64_t data in pending " ++ "attributes item data"; + messages::internalError(asyncResp->res); ++ return; + } ++ ++ attributesJson.emplace( ++ pendingAttributesItem.first, ++ *currValue); + } +- }, +- service, "/xyz/openbmc_project/bios_config/manager", +- "org.freedesktop.DBus.Properties", "Get", +- "xyz.openbmc_project.BIOSConfig.Manager", +- "PendingAttributes"); +- }, +- "xyz.openbmc_project.ObjectMapper", +- "/xyz/openbmc_project/object_mapper", +- "xyz.openbmc_project.ObjectMapper", "GetObject", +- "/xyz/openbmc_project/bios_config/manager", +- std::array<const char*, 0>()); +- }); ++ else ++ { ++ BMCWEB_LOG_ERROR ++ << "Unsupported attribute type."; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ } ++ }, ++ service, "/xyz/openbmc_project/bios_config/manager", ++ "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.BIOSConfig.Manager", ++ "PendingAttributes"); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject", ++ "/xyz/openbmc_project/bios_config/manager", ++ std::array<const char*, 0>()); ++ }); + + BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/Settings") +- .privileges({{"ConfigureComponents"}}) +- .methods(boost::beast::http::verb::patch)( +- [](const crow::Request& req, +- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { +- nlohmann::json inpJson; ++ .privileges(redfish::privileges::patchBios) ++ .methods( ++ boost::beast::http::verb:: ++ patch)([](const crow::Request& req, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ nlohmann::json inpJson; + +- if (!redfish::json_util::readJson(req, asyncResp->res, "data", +- inpJson)) +- { +- return; +- } ++ if (!redfish::json_util::readJson(req, asyncResp->res, "data", ++ inpJson)) ++ { ++ BMCWEB_LOG_ERROR << "No 'data' in req!"; ++ return; ++ } + +- for (auto& attrInfo : inpJson) +- { +- std::optional<std::string> attrName; +- std::optional<std::string> attrType; +- std::optional<std::string> attrValue; +- if (!json_util::getValueFromJsonObject( +- attrInfo, "AttributeName", attrName)) +- { +- messages::propertyMissing(asyncResp->res, +- "AttributeName"); +- return; +- } +- if (!json_util::getValueFromJsonObject( +- attrInfo, "AttributeType", attrType)) +- { +- messages::propertyMissing(asyncResp->res, +- "AttributeType"); +- return; +- } +- if (!json_util::getValueFromJsonObject( +- attrInfo, "AttributeValue", attrValue)) ++ if (inpJson.empty()) ++ { ++ messages::invalidObject(asyncResp->res, "data"); ++ BMCWEB_LOG_ERROR << "No input in req!"; ++ return; ++ } ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, inpJson](const boost::system::error_code ec, ++ const GetObjectType& getObjectType) { ++ if (ec) + { +- messages::propertyMissing(asyncResp->res, +- "AttributeValue"); ++ BMCWEB_LOG_ERROR ++ << "ObjectMapper::GetObject call failed: " << ec; ++ messages::internalError(asyncResp->res); ++ + return; + } +- std::string biosAttrType = mapRedfishToAttrType(*attrType); + +- if (biosAttrType == "UNKNOWN") ++ if (getObjectType.empty()) + { +- BMCWEB_LOG_ERROR << "Invalid attribute type"; +- messages::propertyValueNotInList( +- asyncResp->res, "AttributeType", *attrType); ++ BMCWEB_LOG_ERROR << "getObjectType is empty."; ++ messages::internalError(asyncResp->res); ++ + return; + } + +- PendingAttributesType pendingAttributes; +- pendingAttributes.emplace_back(std::make_pair( +- *attrName, std::make_tuple(biosAttrType, *attrValue))); ++ std::string service = getObjectType.begin()->first; + + crow::connections::systemBus->async_method_call( +- [asyncResp](const boost::system::error_code ec) { ++ [asyncResp, ++ inpJson](const boost::system::error_code ec, ++ const std::variant<BiosBaseTableType>& ++ retBiosTable) { + if (ec) + { + BMCWEB_LOG_ERROR +- << "doPatch resp_handler got error " << ec; ++ << "getBiosAttributes DBUS error: " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ const BiosBaseTableType* baseBiosTable = ++ std::get_if<BiosBaseTableType>(&retBiosTable); ++ ++ if (baseBiosTable == nullptr) ++ { ++ BMCWEB_LOG_ERROR << "baseBiosTable is empty."; + messages::internalError(asyncResp->res); + return; + } ++ ++ PendingAttributesType pendingAttributes{}; ++ ++ for (nlohmann::detail::iteration_proxy_value< ++ nlohmann::detail::iter_impl< ++ const nlohmann::basic_json<>>>& ++ attributes : inpJson.items()) ++ { ++ BiosBaseTableType::const_iterator knobIter = ++ baseBiosTable->find(attributes.key()); ++ if (knobIter == baseBiosTable->end()) ++ { ++ BMCWEB_LOG_ERROR << "Cannot find " ++ << attributes.key() ++ << " in baseBiosTable"; ++ messages::propertyValueNotInList( ++ asyncResp->res, attributes.key(), ++ "data"); ++ return; ++ } ++ ++ const std::string& itemType = ++ std::get<biosBaseAttrType>( ++ knobIter->second); ++ std::string attrType = ++ mapAttrTypeToRedfish(itemType); ++ ++ if (attrType == "String") ++ { ++ std::string val = attributes.value(); ++ ++ pendingAttributes.emplace( ++ attributes.key(), ++ std::make_tuple(itemType, val)); ++ } ++ else if (attrType == "Integer") ++ { ++ pendingAttributes.emplace( ++ attributes.key(), ++ std::make_tuple( ++ itemType, static_cast<int64_t>( ++ attributes.value()))); ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "UNKNOWN attrType == " ++ << itemType; ++ messages::internalError(asyncResp->res); ++ ++ return; ++ } ++ } ++ ++ if (pendingAttributes.empty()) ++ { ++ BMCWEB_LOG_ERROR ++ << "pendingAttributes is empty."; ++ messages::invalidObject(asyncResp->res, "data"); ++ } ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp]( ++ const boost::system::error_code ec) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR ++ << "doPatch resp_handler got error " ++ << ec << "\n"; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ messages::success(asyncResp->res); ++ }, ++ "xyz.openbmc_project.BIOSConfigManager", ++ "/xyz/openbmc_project/bios_config/manager", ++ "org.freedesktop.DBus.Properties", "Set", ++ "xyz.openbmc_project.BIOSConfig.Manager", ++ "PendingAttributes", ++ std::variant<PendingAttributesType>( ++ pendingAttributes)); + }, +- "xyz.openbmc_project.BIOSConfigManager", +- "/xyz/openbmc_project/bios_config/manager", +- "org.freedesktop.DBus.Properties", "Set", ++ service, "/xyz/openbmc_project/bios_config/manager", ++ "org.freedesktop.DBus.Properties", "Get", + "xyz.openbmc_project.BIOSConfig.Manager", +- "PendingAttributes", +- std::variant<PendingAttributesType>(pendingAttributes)); +- } +- }); ++ "BaseBIOSTable"); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject", ++ "/xyz/openbmc_project/bios_config/manager", ++ std::array<const char*, 0>()); ++ }); + } + /** + * BiosAttributeRegistry class supports handle get method for BIOS attribute +@@ -555,6 +644,15 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + + return; + } ++ ++ if (getObjectType.empty()) ++ { ++ BMCWEB_LOG_ERROR << "getObjectType is empty."; ++ messages::internalError(asyncResp->res); ++ ++ return; ++ } ++ + std::string service = getObjectType.begin()->first; + + crow::connections::systemBus->async_method_call( +@@ -575,8 +673,6 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + nlohmann::json& attributeArray = + asyncResp->res + .jsonValue["RegistryEntries"]["Attributes"]; +- nlohmann::json optionsArray = +- nlohmann::json::array(); + if (baseBiosTable == nullptr) + { + BMCWEB_LOG_ERROR << "baseBiosTable == nullptr "; +@@ -592,10 +688,11 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + mapAttrTypeToRedfish(itemType); + if (attrType == "UNKNOWN") + { +- BMCWEB_LOG_ERROR << "attrType == UNKNOWN"; +- messages::internalError(asyncResp->res); +- return; ++ BMCWEB_LOG_ERROR << "UNKNOWN attrType == " ++ << itemType; ++ continue; + } ++ + nlohmann::json attributeItem; + attributeItem["AttributeName"] = item.first; + attributeItem["Type"] = attrType; +@@ -615,10 +712,30 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + std::get_if<std::string>( + &std::get<biosBaseCurrValue>( + item.second)); ++ ++ if (!currValue) ++ { ++ BMCWEB_LOG_ERROR ++ << "Unable to get currValue, no " ++ "std::string data in BIOS " ++ "attributes item data"; ++ continue; ++ } ++ + const std::string* defValue = + std::get_if<std::string>( + &std::get<biosBaseDefaultValue>( + item.second)); ++ ++ if (!defValue) ++ { ++ BMCWEB_LOG_ERROR ++ << "Unable to get defValue, no " ++ "std::string data in BIOS " ++ "attributes item data"; ++ continue; ++ } ++ + attributeItem["CurrentValue"] = + currValue != nullptr ? *currValue : ""; + attributeItem["DefaultValue"] = +@@ -630,10 +747,30 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + std::get_if<int64_t>( + &std::get<biosBaseCurrValue>( + item.second)); ++ ++ if (!currValue) ++ { ++ BMCWEB_LOG_ERROR ++ << "Unable to get currValue, no " ++ "int64_t data in BIOS " ++ "attributes item data"; ++ continue; ++ } ++ + const int64_t* defValue = + std::get_if<int64_t>( + &std::get<biosBaseDefaultValue>( + item.second)); ++ ++ if (!defValue) ++ { ++ BMCWEB_LOG_ERROR ++ << "Unable to get defValue, no " ++ "int64_t data in BIOS " ++ "attributes item data"; ++ continue; ++ } ++ + attributeItem["CurrentValue"] = + currValue != nullptr ? *currValue : 0; + attributeItem["DefaultValue"] = +@@ -641,12 +778,13 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + } + else + { +- BMCWEB_LOG_ERROR +- << "Unsupported attribute type."; +- messages::internalError(asyncResp->res); +- return; ++ BMCWEB_LOG_ERROR << "UNKNOWN attrType == " ++ << itemType; ++ continue; + } + ++ nlohmann::json optionsArray = ++ nlohmann::json::array(); + const std::vector<OptionsItemType>& + optionsVector = + std::get<biosBaseOptions>(item.second); +@@ -661,9 +799,9 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + if (optItemTypeRedfish == "UNKNOWN") + { + BMCWEB_LOG_ERROR +- << "optItemTypeRedfish == UNKNOWN"; +- messages::internalError(asyncResp->res); +- return; ++ << "UNKNOWN optItemTypeRedfish == " ++ << strOptItemType; ++ continue; + } + if (optItemTypeRedfish == "OneOf") + { +@@ -671,6 +809,17 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + std::get_if<std::string>( + &std::get<optItemValue>( + optItem)); ++ ++ if (!currValue) ++ { ++ BMCWEB_LOG_ERROR ++ << "Unable to get currValue, " ++ "no " ++ "std::string data in option " ++ "item value"; ++ continue; ++ } ++ + optItemJson[optItemTypeRedfish] = + currValue != nullptr ? *currValue + : ""; +@@ -681,6 +830,17 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + std::get_if<int64_t>( + &std::get<optItemValue>( + optItem)); ++ ++ if (!currValue) ++ { ++ BMCWEB_LOG_ERROR ++ << "Unable to get currValue, " ++ "no " ++ "int64_t data in option " ++ "item value"; ++ continue; ++ } ++ + optItemJson[optItemTypeRedfish] = + currValue != nullptr ? *currValue + : 0; +@@ -689,6 +849,12 @@ inline void requestRoutesBiosAttributeRegistry(App& app) + optionsArray.push_back(optItemJson); + } + ++ if (optionsArray.empty()) ++ { ++ BMCWEB_LOG_ERROR << "optionsArray is empty"; ++ continue; ++ } ++ + attributeItem["Value"] = optionsArray; + attributeArray.push_back(attributeItem); + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket new file mode 100644 index 000000000..8782e4dd3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket @@ -0,0 +1,9 @@ +[Unit] +Description=BMC Webserver socket + +[Socket] +ListenStream=443 +ReusePort=true + +[Install] +WantedBy=sockets.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch new file mode 100644 index 000000000..52135e255 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch @@ -0,0 +1,121 @@ +From 6ff897d2b5513f15445f18aae16d8439ed94f377 Mon Sep 17 00:00:00 2001 +From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +Date: Mon, 11 Oct 2021 18:41:27 +0530 +Subject: [PATCH] Add unmerged changes for http retry support + +The http retry support added upstream as a single patch was slpit into +3 patches, but only 2 of them was merged. +This commit pulls in the differentail changes required to complete the +entire http retry support. and also allow for other subsequent patches +to be appplied easily. + +Change-Id: Id8ccd991b7ffc505196b1a92b23e1cd51e00bc89 +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/http_client.hpp | 44 +++++++++++-------- + .../include/event_service_manager.hpp | 2 +- + 2 files changed, 27 insertions(+), 19 deletions(-) + +diff --git a/http/http_client.hpp b/http/http_client.hpp +index ab20eb0..aad1cce 100644 +--- a/http/http_client.hpp ++++ b/http/http_client.hpp +@@ -68,7 +68,6 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + std::optional< + boost::beast::http::response_parser<boost::beast::http::string_body>> + parser; +- std::vector<std::pair<std::string, std::string>> headers; + boost::circular_buffer_space_optimized<std::string> requestDataQueue{}; + + ConnState state; +@@ -137,18 +136,6 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + + BMCWEB_LOG_DEBUG << __FUNCTION__ << "(): " << host << ":" << port; + +- req.version(static_cast<int>(11)); // HTTP 1.1 +- req.target(uri); +- req.method(boost::beast::http::verb::post); +- +- // Set headers +- for (const auto& [key, value] : headers) +- { +- req.set(key, value); +- } +- req.set(boost::beast::http::field::host, host); +- req.keep_alive(true); +- + req.body() = data; + req.prepare_payload(); + +@@ -204,6 +191,17 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + BMCWEB_LOG_DEBUG << "recvMessage() data: " + << self->parser->get(); + ++ // Check if the response and header are received ++ if (!self->parser->is_done()) ++ { ++ // The parser failed to receive the response ++ BMCWEB_LOG_ERROR ++ << "recvMessage() parser failed to receive response"; ++ self->state = ConnState::recvFailed; ++ self->handleConnState(); ++ return; ++ } ++ + unsigned int respCode = self->parser->get().result_int(); + BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " + << respCode; +@@ -398,11 +396,17 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + const std::string& destIP, const std::string& destPort, + const std::string& destUri) : + conn(ioc), +- timer(ioc), subId(id), host(destIP), port(destPort), uri(destUri), +- retryCount(0), maxRetryAttempts(5), retryIntervalSecs(0), ++ timer(ioc), req(boost::beast::http::verb::post, destUri, 11), ++ state(ConnState::initialized), subId(id), host(destIP), port(destPort), ++ uri(destUri), retryCount(0), maxRetryAttempts(5), retryIntervalSecs(0), + retryPolicyAction("TerminateAfterRetries"), runningTimer(false) + { +- state = ConnState::initialized; ++ // Set the request header ++ req.set(boost::beast::http::field::host, host); ++ req.set(boost::beast::http::field::content_type, "application/json"); ++ req.keep_alive(true); ++ ++ requestDataQueue.set_capacity(maxRequestQueueSize); + } + + void sendData(const std::string& data) +@@ -425,10 +429,14 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + return; + } + +- void setHeaders( ++ void addHeaders( + const std::vector<std::pair<std::string, std::string>>& httpHeaders) + { +- headers = httpHeaders; ++ // Set custom headers ++ for (const auto& [key, value] : httpHeaders) ++ { ++ req.set(key, value); ++ } + } + + void setRetryConfig(const uint32_t retryAttempts, +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 8042803..0a63b8c 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -412,7 +412,7 @@ class Subscription : public persistent_data::UserSubscription + reqHeaders.emplace_back(std::pair(key, val)); + } + } +- conn->setHeaders(reqHeaders); ++ conn->addHeaders(reqHeaders); + conn->sendData(msg); + this->eventSeqNum++; + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch new file mode 100644 index 000000000..aeeafc421 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch @@ -0,0 +1,453 @@ +From 3f2ad28e6e124249cde3df50c9e18c283fbcbf3e Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Mon, 22 Feb 2021 17:07:47 +0000 +Subject: [PATCH] EventService: https client support + +Add https client support for push style eventing. Using this BMC can +push the event logs/telemetry data to event listener over secure http +channel. + +Tested: + - Created subscription with https destination url. Using + SubmitTestEvent action set the event and can see event on event + listener. + - Validator passed. + +Change-Id: I44c3918b39baa2eb5fddda9d635f99aa280a422a +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +--- + http/http_client.hpp | 307 ++++++++++++------ + .../include/event_service_manager.hpp | 2 +- + 2 files changed, 202 insertions(+), 107 deletions(-) + +diff --git a/http/http_client.hpp b/http/http_client.hpp +index aad1cce..5e7ff47 100644 +--- a/http/http_client.hpp ++++ b/http/http_client.hpp +@@ -20,6 +20,7 @@ + #include <boost/beast/core/flat_buffer.hpp> + #include <boost/beast/core/tcp_stream.hpp> + #include <boost/beast/http/message.hpp> ++#include <boost/beast/ssl/ssl_stream.hpp> + #include <boost/beast/version.hpp> + #include <include/async_resolve.hpp> + +@@ -43,6 +44,8 @@ enum class ConnState + resolveFailed, + connectInProgress, + connectFailed, ++ handshakeInProgress, ++ handshakeFailed, + connected, + sendInProgress, + sendFailed, +@@ -61,7 +64,9 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + { + private: + crow::async_resolve::Resolver resolver; ++ boost::asio::ssl::context ctx{boost::asio::ssl::context::tlsv12_client}; + boost::beast::tcp_stream conn; ++ std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream&>> sslConn; + boost::asio::steady_timer timer; + boost::beast::flat_static_buffer<httpReadBodyLimit> buffer; + boost::beast::http::request<boost::beast::http::string_body> req; +@@ -108,23 +113,52 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + const std::vector<boost::asio::ip::tcp::endpoint>& endpointList) + { + state = ConnState::connectInProgress; ++ sslConn.emplace(conn, ctx); + + BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port; ++ auto respHandler = [self(shared_from_this())]( ++ const boost::beast::error_code ec, ++ const boost::asio::ip::tcp::endpoint& endpoint) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Connect " << endpoint ++ << " failed: " << ec.message(); ++ self->state = ConnState::connectFailed; ++ self->handleConnState(); ++ return; ++ } + ++ BMCWEB_LOG_DEBUG << "Connected to: " << endpoint; ++ if (self->sslConn) ++ { ++ self->performHandshake(); ++ } ++ else ++ { ++ self->handleConnState(); ++ } ++ }; + conn.expires_after(std::chrono::seconds(30)); +- conn.async_connect( +- endpointList, [self(shared_from_this())]( +- const boost::beast::error_code ec, +- const boost::asio::ip::tcp::endpoint& endpoint) { ++ conn.async_connect(endpointList, std::move(respHandler)); ++ } ++ ++ void performHandshake() ++ { ++ state = ConnState::handshakeInProgress; ++ ++ sslConn->async_handshake( ++ boost::asio::ssl::stream_base::client, ++ [self(shared_from_this())](const boost::beast::error_code ec) { + if (ec) + { +- BMCWEB_LOG_ERROR << "Connect " << endpoint +- << " failed: " << ec.message(); +- self->state = ConnState::connectFailed; ++ BMCWEB_LOG_ERROR << "SSL handshake failed: " ++ << ec.message(); ++ self->state = ConnState::handshakeFailed; + self->handleConnState(); + return; + } +- BMCWEB_LOG_DEBUG << "Connected to: " << endpoint; ++ ++ BMCWEB_LOG_DEBUG << "SSL Handshake successfull"; + self->state = ConnState::connected; + self->handleConnState(); + }); +@@ -132,132 +166,187 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + + void sendMessage(const std::string& data) + { +- state = ConnState::sendInProgress; +- + BMCWEB_LOG_DEBUG << __FUNCTION__ << "(): " << host << ":" << port; ++ state = ConnState::sendInProgress; + + req.body() = data; + req.prepare_payload(); + +- // Set a timeout on the operation +- conn.expires_after(std::chrono::seconds(30)); ++ auto respHandler = [self(shared_from_this())]( ++ const boost::beast::error_code ec, ++ const std::size_t& bytesTransferred) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message(); ++ self->state = ConnState::sendFailed; ++ self->handleConnState(); ++ return; ++ } + +- // Send the HTTP request to the remote host +- boost::beast::http::async_write( +- conn, req, +- [self(shared_from_this())](const boost::beast::error_code& ec, +- const std::size_t& bytesTransferred) { +- if (ec) +- { +- BMCWEB_LOG_ERROR << "sendMessage() failed: " +- << ec.message(); +- self->state = ConnState::sendFailed; +- self->handleConnState(); +- return; +- } +- BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: " +- << bytesTransferred; +- boost::ignore_unused(bytesTransferred); ++ BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: " ++ << bytesTransferred; ++ boost::ignore_unused(bytesTransferred); ++ self->recvMessage(); ++ }; + +- self->recvMessage(); +- }); ++ // Set a timeout on the operation ++ conn.expires_after(std::chrono::seconds(30)); ++ if (sslConn) ++ { ++ boost::beast::http::async_write(*sslConn, req, ++ std::move(respHandler)); ++ } ++ else ++ { ++ boost::beast::http::async_write(conn, req, std::move(respHandler)); ++ } + } +- + void recvMessage() + { + state = ConnState::recvInProgress; + ++ auto respHandler = [self(shared_from_this())]( ++ const boost::beast::error_code ec, ++ const std::size_t& bytesTransferred) { ++ if (ec && ec != boost::asio::ssl::error::stream_truncated) ++ { ++ BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message(); ++ ++ self->state = ConnState::recvFailed; ++ self->handleConnState(); ++ return; ++ } ++ ++ BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: " ++ << bytesTransferred; ++ boost::ignore_unused(bytesTransferred); ++ ++ // Check if the response and header are received ++ if (!self->parser->is_done()) ++ { ++ // The parser failed to receive the response ++ BMCWEB_LOG_ERROR ++ << "recvMessage() parser failed to receive response"; ++ self->state = ConnState::recvFailed; ++ self->handleConnState(); ++ return; ++ } ++ ++ unsigned int respCode = self->parser->get().result_int(); ++ BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " ++ << respCode; ++ ++ // 2XX response is considered to be successful ++ if ((respCode < 200) || (respCode >= 300)) ++ { ++ // The listener failed to receive the Sent-Event ++ BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to " ++ "receive Sent-Event"; ++ self->state = ConnState::recvFailed; ++ self->handleConnState(); ++ return; ++ } ++ ++ // Send is successful, Lets remove data from queue ++ // check for next request data in queue. ++ if (!self->requestDataQueue.empty()) ++ { ++ self->requestDataQueue.pop_front(); ++ } ++ self->state = ConnState::idle; ++ // Keep the connection alive if server supports it ++ // Else close the connection ++ BMCWEB_LOG_DEBUG << "recvMessage() keepalive : " ++ << self->parser->keep_alive(); ++ if (!self->parser->keep_alive()) ++ { ++ // Abort the connection since server is not keep-alive enabled ++ self->state = ConnState::abortConnection; ++ } ++ ++ // Returns ownership of the parsed message ++ self->parser->release(); ++ ++ self->handleConnState(); ++ }; + parser.emplace(std::piecewise_construct, std::make_tuple()); + parser->body_limit(httpReadBodyLimit); + + // Check only for the response header + parser->skip(true); ++ conn.expires_after(std::chrono::seconds(30)); ++ if (sslConn) ++ { ++ boost::beast::http::async_read(*sslConn, buffer, *parser, ++ std::move(respHandler)); ++ } ++ else ++ { ++ boost::beast::http::async_read(conn, buffer, *parser, ++ std::move(respHandler)); ++ } ++ } ++ void doClose() ++ { ++ state = ConnState::closeInProgress; + +- // Receive the HTTP response +- boost::beast::http::async_read( +- conn, buffer, *parser, +- [self(shared_from_this())](const boost::beast::error_code& ec, +- const std::size_t& bytesTransferred) { ++ // Set the timeout on the tcp stream socket for the async operation ++ conn.expires_after(std::chrono::seconds(30)); ++ if (sslConn) ++ { ++ sslConn->async_shutdown([self = shared_from_this()]( ++ const boost::system::error_code ec) { + if (ec) + { +- BMCWEB_LOG_ERROR << "recvMessage() failed: " +- << ec.message(); +- self->state = ConnState::recvFailed; +- self->handleConnState(); +- return; ++ // Many https server closes connection abruptly ++ // i.e witnout close_notify. More details are at ++ // https://github.com/boostorg/beast/issues/824 ++ if (ec == boost::asio::ssl::error::stream_truncated) ++ { ++ BMCWEB_LOG_INFO << "doClose(): Connection " ++ "closed by server. "; ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "doClose() failed: " ++ << ec.message(); ++ } + } +- BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: " +- << bytesTransferred; +- BMCWEB_LOG_DEBUG << "recvMessage() data: " +- << self->parser->get(); +- +- // Check if the response and header are received +- if (!self->parser->is_done()) ++ else + { +- // The parser failed to receive the response +- BMCWEB_LOG_ERROR +- << "recvMessage() parser failed to receive response"; +- self->state = ConnState::recvFailed; +- self->handleConnState(); +- return; ++ BMCWEB_LOG_DEBUG << "Connection closed gracefully..."; + } ++ self->conn.close(); + +- unsigned int respCode = self->parser->get().result_int(); +- BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: " +- << respCode; +- +- // 2XX response is considered to be successful +- if ((respCode < 200) || (respCode >= 300)) ++ if ((self->state != ConnState::suspended) && ++ (self->state != ConnState::terminated)) + { +- // The listener failed to receive the Sent-Event +- BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to " +- "receive Sent-Event"; +- self->state = ConnState::recvFailed; ++ self->state = ConnState::closed; + self->handleConnState(); +- return; + } +- +- // Send is successful, Lets remove data from queue +- // check for next request data in queue. +- if (!self->requestDataQueue.empty()) +- { +- self->requestDataQueue.pop_front(); +- } +- self->state = ConnState::idle; +- +- // Keep the connection alive if server supports it +- // Else close the connection +- BMCWEB_LOG_DEBUG << "recvMessage() keepalive : " +- << self->parser->keep_alive(); +- if (!self->parser->keep_alive()) +- { +- // Abort the connection since server is not keep-alive +- // enabled +- self->state = ConnState::abortConnection; +- } +- +- self->handleConnState(); + }); +- } +- +- void doClose() +- { +- state = ConnState::closeInProgress; +- boost::beast::error_code ec; +- conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); +- conn.close(); +- +- // not_connected happens sometimes so don't bother reporting it. +- if (ec && ec != boost::beast::errc::not_connected) +- { +- BMCWEB_LOG_ERROR << "shutdown failed: " << ec.message(); +- return; + } +- BMCWEB_LOG_DEBUG << "Connection closed gracefully"; +- if ((state != ConnState::suspended) && (state != ConnState::terminated)) ++ else + { +- state = ConnState::closed; +- handleConnState(); ++ boost::beast::error_code ec; ++ conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ++ ec); ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "doClose() failed: " << ec.message(); ++ } ++ else ++ { ++ BMCWEB_LOG_DEBUG << "Connection closed gracefully..."; ++ } ++ conn.close(); ++ ++ if ((state != ConnState::suspended) && ++ (state != ConnState::terminated)) ++ { ++ state = ConnState::closed; ++ handleConnState(); ++ } + } + } + +@@ -330,6 +419,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + { + case ConnState::resolveInProgress: + case ConnState::connectInProgress: ++ case ConnState::handshakeInProgress: + case ConnState::sendInProgress: + case ConnState::recvInProgress: + case ConnState::closeInProgress: +@@ -356,6 +446,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + } + case ConnState::resolveFailed: + case ConnState::connectFailed: ++ case ConnState::handshakeFailed: + case ConnState::sendFailed: + case ConnState::recvFailed: + case ConnState::retry: +@@ -394,7 +485,8 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + public: + explicit HttpClient(boost::asio::io_context& ioc, const std::string& id, + const std::string& destIP, const std::string& destPort, +- const std::string& destUri) : ++ const std::string& destUri, ++ const std::string& uriProto) : + conn(ioc), + timer(ioc), req(boost::beast::http::verb::post, destUri, 11), + state(ConnState::initialized), subId(id), host(destIP), port(destPort), +@@ -407,8 +499,11 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + req.keep_alive(true); + + requestDataQueue.set_capacity(maxRequestQueueSize); ++ if (uriProto == "https") ++ { ++ sslConn.emplace(conn, ctx); ++ } + } +- + void sendData(const std::string& data) + { + if ((state == ConnState::suspended) || (state == ConnState::terminated)) +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 08d0b98..f1ce0c0 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -385,7 +385,7 @@ class Subscription : public persistent_data::UserSubscription + { + conn = std::make_shared<crow::HttpClient>( + crow::connections::systemBus->get_io_context(), id, host, port, +- path); ++ path, uriProto); + } + + Subscription(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) : +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch new file mode 100644 index 000000000..ea521a7e4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch @@ -0,0 +1,468 @@ +From d7a2660f200c38e74bfcbfe55b8da1b8bed08833 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Fri, 12 Mar 2021 18:53:25 +0000 +Subject: [PATCH] Add Server-Sent-Events support + +Server-Sent Events is a standard describing how servers can +initiate data transmission towards clients once an initial +client connection has been established. Unlike websockets +(which are bidirectional), Server-Sent Events are +unidirectional and commonly used to send message updates or +continuous data streams to a browser client. + +This is base patch for adding Server-Sent events support to +bmcweb. Redfish eventservice SSE style subscription uses +this and will be loaded on top of this commit. + +Tested: + - Tested using follow-up patch on top which adds + support for Redfish EventService SSE style subscription + and observed events are getting sent periodically. + +Change-Id: I36956565cbba30c2007852c9471f477f6d1736e9 +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/http_connection.hpp | 10 +- + http/http_response.hpp | 7 +- + http/routing.hpp | 71 ++++++++++ + http/server_sent_event.hpp | 279 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 362 insertions(+), 5 deletions(-) + create mode 100644 http/server_sent_event.hpp + +diff --git a/http/http_connection.hpp b/http/http_connection.hpp +index 8e53afa..a1bbfce 100644 +--- a/http/http_connection.hpp ++++ b/http/http_connection.hpp +@@ -378,11 +378,13 @@ class Connection : + [self] { self->completeRequest(); }); + }); + +- if (thisReq.isUpgrade() && +- boost::iequals( +- thisReq.getHeaderValue(boost::beast::http::field::upgrade), +- "websocket")) ++ if ((thisReq.isUpgrade() && ++ boost::iequals( ++ thisReq.getHeaderValue(boost::beast::http::field::upgrade), ++ "websocket")) || ++ (req->url == "/sse")) + { ++ BMCWEB_LOG_DEBUG << "Request: " << this << " is getting upgraded"; + handler->handleUpgrade(thisReq, res, std::move(adaptor)); + // delete lambda with self shared_ptr + // to enable connection destruction +diff --git a/http/http_response.hpp b/http/http_response.hpp +index a983d4a..07b0265 100644 +--- a/http/http_response.hpp ++++ b/http/http_response.hpp +@@ -15,10 +15,15 @@ namespace crow + template <typename Adaptor, typename Handler> + class Connection; + ++template <typename Adaptor> ++class SseConnectionImpl; ++ + struct Response + { + template <typename Adaptor, typename Handler> + friend class crow::Connection; ++ template <typename Adaptor> ++ friend class crow::SseConnectionImpl; + using response_type = + boost::beast::http::response<boost::beast::http::string_body>; + +@@ -143,8 +148,8 @@ struct Response + + private: + bool completed{}; +- std::function<void()> completeRequestHandler; + std::function<bool()> isAliveHelper; ++ std::function<void()> completeRequestHandler; + + // In case of a JSON object, set the Content-Type header + void jsonMode() +diff --git a/http/routing.hpp b/http/routing.hpp +index 5d9c8e3..bfff107 100644 +--- a/http/routing.hpp ++++ b/http/routing.hpp +@@ -6,6 +6,7 @@ + #include "http_response.hpp" + #include "logging.hpp" + #include "privileges.hpp" ++#include "server_sent_event.hpp" + #include "sessions.hpp" + #include "utility.hpp" + #include "websocket.hpp" +@@ -398,6 +399,68 @@ class WebSocketRule : public BaseRule + std::function<void(crow::websocket::Connection&)> errorHandler; + }; + ++class SseSocketRule : public BaseRule ++{ ++ using self_t = SseSocketRule; ++ ++ public: ++ SseSocketRule(const std::string& ruleIn) : BaseRule(ruleIn) ++ {} ++ ++ void validate() override ++ {} ++ ++ void handle(const Request&, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ const RoutingParams&) override ++ { ++ asyncResp->res.result(boost::beast::http::status::not_found); ++ } ++ ++ void handleUpgrade(const Request& req, Response&, ++ boost::asio::ip::tcp::socket&& adaptor) override ++ { ++ std::shared_ptr<crow::SseConnectionImpl<boost::asio::ip::tcp::socket>> ++ myConnection = std::make_shared< ++ crow::SseConnectionImpl<boost::asio::ip::tcp::socket>>( ++ req, std::move(adaptor), openHandler, closeHandler); ++ myConnection->start(); ++ } ++#ifdef BMCWEB_ENABLE_SSL ++ void handleUpgrade(const Request& req, Response&, ++ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& ++ adaptor) override ++ { ++ std::shared_ptr<crow::SseConnectionImpl< ++ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>> ++ myConnection = std::make_shared<crow::SseConnectionImpl< ++ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>( ++ req, std::move(adaptor), openHandler, closeHandler); ++ myConnection->start(); ++ } ++#endif ++ ++ template <typename Func> ++ self_t& onopen(Func f) ++ { ++ openHandler = f; ++ return *this; ++ } ++ ++ template <typename Func> ++ self_t& onclose(Func f) ++ { ++ closeHandler = f; ++ return *this; ++ } ++ ++ private: ++ std::function<void(std::shared_ptr<crow::SseConnection>&, ++ const crow::Request&, crow::Response&)> ++ openHandler; ++ std::function<void(std::shared_ptr<crow::SseConnection>&)> closeHandler; ++}; ++ + template <typename T> + struct RuleParameterTraits + { +@@ -410,6 +473,14 @@ struct RuleParameterTraits + return *p; + } + ++ SseSocketRule& serverSentEvent() ++ { ++ self_t* self = static_cast<self_t*>(this); ++ SseSocketRule* p = new SseSocketRule(self->rule); ++ self->ruleToUpgrade.reset(p); ++ return *p; ++ } ++ + self_t& name(const std::string_view name) noexcept + { + self_t* self = static_cast<self_t*>(this); +diff --git a/http/server_sent_event.hpp b/http/server_sent_event.hpp +new file mode 100644 +index 0000000..41d18ed +--- /dev/null ++++ b/http/server_sent_event.hpp +@@ -0,0 +1,279 @@ ++#pragma once ++#include "http_request.hpp" ++ ++#include <boost/algorithm/string/predicate.hpp> ++#include <boost/asio/buffer.hpp> ++#include <boost/beast/http/buffer_body.hpp> ++#include <boost/beast/websocket.hpp> ++ ++#include <array> ++#include <functional> ++ ++#ifdef BMCWEB_ENABLE_SSL ++#include <boost/beast/websocket/ssl.hpp> ++#endif ++ ++namespace crow ++{ ++ ++struct SseConnection : std::enable_shared_from_this<SseConnection> ++{ ++ public: ++ SseConnection(const crow::Request& reqIn) : req(reqIn) ++ {} ++ virtual ~SseConnection() = default; ++ ++ virtual boost::asio::io_context& getIoContext() = 0; ++ virtual void sendSSEHeader() = 0; ++ virtual void completeRequest() = 0; ++ virtual void close(const std::string_view msg = "quit") = 0; ++ virtual void sendEvent(const std::string_view id, ++ const std::string_view msg) = 0; ++ ++ crow::Request req; ++ crow::Response res; ++}; ++ ++template <typename Adaptor> ++class SseConnectionImpl : public SseConnection ++{ ++ public: ++ SseConnectionImpl( ++ const crow::Request& reqIn, Adaptor adaptorIn, ++ std::function<void(std::shared_ptr<SseConnection>&, ++ const crow::Request&, crow::Response&)> ++ openHandler, ++ std::function<void(std::shared_ptr<SseConnection>&)> closeHandler) : ++ SseConnection(reqIn), ++ adaptor(std::move(adaptorIn)), openHandler(std::move(openHandler)), ++ closeHandler(std::move(closeHandler)) ++ { ++ BMCWEB_LOG_DEBUG << "SseConnectionImpl: SSE constructor " << this; ++ } ++ ++ ~SseConnectionImpl() override ++ { ++ res.completeRequestHandler = nullptr; ++ BMCWEB_LOG_DEBUG << "SseConnectionImpl: SSE destructor " << this; ++ } ++ ++ boost::asio::io_context& getIoContext() override ++ { ++ return static_cast<boost::asio::io_context&>( ++ adaptor.get_executor().context()); ++ } ++ ++ void start() ++ { ++ // Register for completion callback. ++ res.completeRequestHandler = [this, self(shared_from_this())] { ++ boost::asio::post(this->adaptor.get_executor(), ++ [self] { self->completeRequest(); }); ++ }; ++ ++ if (openHandler) ++ { ++ std::shared_ptr<SseConnection> self = this->shared_from_this(); ++ openHandler(self, req, res); ++ } ++ } ++ ++ void close(const std::string_view msg) override ++ { ++ BMCWEB_LOG_DEBUG << "Closing SSE connection " << this << " - " << msg; ++ boost::beast::get_lowest_layer(adaptor).close(); ++ ++ // send notification to handler for cleanup ++ if (closeHandler) ++ { ++ std::shared_ptr<SseConnection> self = this->shared_from_this(); ++ closeHandler(self); ++ } ++ } ++ ++ void sendSSEHeader() override ++ { ++ BMCWEB_LOG_DEBUG << "Starting SSE connection"; ++ using BodyType = boost::beast::http::buffer_body; ++ auto response = ++ std::make_shared<boost::beast::http::response<BodyType>>( ++ boost::beast::http::status::ok, 11); ++ auto serializer = ++ std::make_shared<boost::beast::http::response_serializer<BodyType>>( ++ *response); ++ ++ response->set(boost::beast::http::field::server, "bmcweb"); ++ response->set(boost::beast::http::field::content_type, ++ "text/event-stream"); ++ response->body().data = nullptr; ++ response->body().size = 0; ++ response->body().more = true; ++ ++ boost::beast::http::async_write_header( ++ adaptor, *serializer, ++ [this, self(shared_from_this()), response, serializer]( ++ const boost::beast::error_code& ec, const std::size_t&) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Error sending header" << ec; ++ close("async_write_header failed"); ++ return; ++ } ++ BMCWEB_LOG_DEBUG << "SSE header sent - Connection established"; ++ ++ // SSE stream header sent, So lets setup monitor. ++ // Any read data on this stream will be error in case of SSE. ++ setupRead(); ++ }); ++ } ++ ++ void setupRead() ++ { ++ adaptor.async_read_some( ++ outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()), ++ [this](const boost::system::error_code& ec, std::size_t bytesRead) { ++ BMCWEB_LOG_DEBUG << "async_read_some: Read " << bytesRead ++ << " bytes"; ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Read error: " << ec; ++ } ++ outputBuffer.commit(bytesRead); ++ outputBuffer.consume(bytesRead); ++ ++ // After establishing SSE stream, Reading data on this ++ // stream means client is disobeys the SSE protocol. ++ // Read the data to avoid buffer attacks and close connection. ++ close("Close SSE connection"); ++ return; ++ }); ++ } ++ ++ void doWrite() ++ { ++ if (doingWrite) ++ { ++ return; ++ } ++ if (inputBuffer.size() == 0) ++ { ++ BMCWEB_LOG_DEBUG << "inputBuffer is empty... Bailing out"; ++ return; ++ } ++ doingWrite = true; ++ ++ adaptor.async_write_some( ++ inputBuffer.data(), [this, self(shared_from_this())]( ++ boost::beast::error_code ec, ++ const std::size_t& bytesTransferred) { ++ doingWrite = false; ++ inputBuffer.consume(bytesTransferred); ++ ++ if (ec == boost::asio::error::eof) ++ { ++ BMCWEB_LOG_ERROR << "async_write_some() SSE stream closed"; ++ close("SSE stream closed"); ++ return; ++ } ++ ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "async_write_some() failed: " ++ << ec.message(); ++ close("async_write_some failed"); ++ return; ++ } ++ BMCWEB_LOG_DEBUG << "async_write_some() bytes transferred: " ++ << bytesTransferred; ++ ++ doWrite(); ++ }); ++ } ++ ++ void completeRequest() override ++ { ++ BMCWEB_LOG_DEBUG << "SSE completeRequest() handler"; ++ if (res.body().empty() && !res.jsonValue.empty()) ++ { ++ res.addHeader("Content-Type", "application/json"); ++ res.body() = res.jsonValue.dump( ++ 2, ' ', true, nlohmann::json::error_handler_t::replace); ++ } ++ ++ res.preparePayload(); ++ auto serializer = ++ std::make_shared<boost::beast::http::response_serializer< ++ boost::beast::http::string_body>>(*res.stringResponse); ++ ++ boost::beast::http::async_write( ++ adaptor, *serializer, ++ [this, self(shared_from_this()), ++ serializer](const boost::system::error_code& ec, ++ std::size_t bytesTransferred) { ++ BMCWEB_LOG_DEBUG << this << " async_write " << bytesTransferred ++ << " bytes"; ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG << this << " from async_write failed"; ++ return; ++ } ++ res.clear(); ++ ++ BMCWEB_LOG_DEBUG << this ++ << " Closing SSE connection - Request invalid"; ++ close("Request invalid"); ++ }); ++ ++ // delete lambda with self shared_ptr ++ // to enable connection destruction ++ res.completeRequestHandler = nullptr; ++ } ++ ++ void sendEvent(const std::string_view id, ++ const std::string_view msg) override ++ { ++ if (msg.empty()) ++ { ++ BMCWEB_LOG_DEBUG << "Empty data, bailing out."; ++ return; ++ } ++ ++ std::string rawData; ++ if (!id.empty()) ++ { ++ rawData += "id: "; ++ rawData.append(id.begin(), id.end()); ++ rawData += "\n"; ++ } ++ ++ rawData += "data: "; ++ for (char character : msg) ++ { ++ rawData += character; ++ if (character == '\n') ++ { ++ rawData += "data: "; ++ } ++ } ++ rawData += "\n\n"; ++ ++ boost::asio::buffer_copy(inputBuffer.prepare(rawData.size()), ++ boost::asio::buffer(rawData)); ++ inputBuffer.commit(rawData.size()); ++ ++ doWrite(); ++ } ++ ++ private: ++ Adaptor adaptor; ++ ++ boost::beast::flat_static_buffer<1024U * 8U> outputBuffer; ++ boost::beast::flat_static_buffer<1024U * 64U> inputBuffer; ++ bool doingWrite = false; ++ ++ std::function<void(std::shared_ptr<SseConnection>&, const crow::Request&, ++ crow::Response&)> ++ openHandler; ++ std::function<void(std::shared_ptr<SseConnection>&)> closeHandler; ++}; ++} // namespace crow +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch new file mode 100644 index 000000000..ee69081ef --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch @@ -0,0 +1,679 @@ +From 799e47842e179f7c752712004f0e96d3219eee11 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Tue, 16 Mar 2021 15:37:24 +0000 +Subject: [PATCH] Add SSE style subscription support to eventservice + +This commit adds the SSE style eventservice subscription +style event. Using this, end user can subscribe for +Redfish event logs using GET on SSE usri from +browser. +URI: /redfish/v1/EventService/Subscriptions/SSE + +Tested: + - From Browser did GET on above SSE URI and + generated some Redfish event logs(power cycle) + and saw redfish event logs streaming on browser. + - After SSE registration, Check Subscription collections + and GET on individual subscription and saw desired + response. + - Ran RedfishValidation and its passed. + +Change-Id: I7f4b7a34974080739c4ba968ed570489af0474de +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/http_connection.hpp | 2 +- + include/eventservice_sse.hpp | 75 +++++ + .../include/event_service_manager.hpp | 109 +++++-- + redfish-core/include/server_sent_events.hpp | 290 ------------------ + redfish-core/lib/event_service.hpp | 8 +- + src/webserver_main.cpp | 2 + + 6 files changed, 164 insertions(+), 322 deletions(-) + create mode 100644 include/eventservice_sse.hpp + delete mode 100644 redfish-core/include/server_sent_events.hpp + +diff --git a/http/http_connection.hpp b/http/http_connection.hpp +index a1bbfce..2d08501 100644 +--- a/http/http_connection.hpp ++++ b/http/http_connection.hpp +@@ -382,7 +382,7 @@ class Connection : + boost::iequals( + thisReq.getHeaderValue(boost::beast::http::field::upgrade), + "websocket")) || +- (req->url == "/sse")) ++ (req->url == "/redfish/v1/EventService/Subscriptions/SSE")) + { + BMCWEB_LOG_DEBUG << "Request: " << this << " is getting upgraded"; + handler->handleUpgrade(thisReq, res, std::move(adaptor)); +diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp +new file mode 100644 +index 0000000..14daf00 +--- /dev/null ++++ b/include/eventservice_sse.hpp +@@ -0,0 +1,75 @@ ++#pragma once ++ ++#include <app.hpp> ++#include <event_service_manager.hpp> ++ ++namespace redfish ++{ ++namespace eventservice_sse ++{ ++ ++static bool createSubscription(std::shared_ptr<crow::SseConnection>& conn, ++ const crow::Request& req, crow::Response& res) ++{ ++ if ((EventServiceManager::getInstance().getNumberOfSubscriptions() >= ++ maxNoOfSubscriptions) || ++ EventServiceManager::getInstance().getNumberOfSSESubscriptions() >= ++ maxNoOfSSESubscriptions) ++ { ++ BMCWEB_LOG_ERROR << "Max SSE subscriptions reached"; ++ messages::eventSubscriptionLimitExceeded(res); ++ res.end(); ++ return false; ++ } ++ BMCWEB_LOG_DEBUG << "Request query param size: " << req.urlParams.size(); ++ ++ std::shared_ptr<redfish::Subscription> subValue = ++ std::make_shared<redfish::Subscription>(std::move(conn)); ++ ++ // GET on this URI means, Its SSE subscriptionType. ++ subValue->subscriptionType = redfish::subscriptionTypeSSE; ++ ++ // TODO: parse $filter query params and fill config. ++ subValue->protocol = "Redfish"; ++ subValue->retryPolicy = "TerminateAfterRetries"; ++ subValue->eventFormatType = "Event"; ++ ++ std::string id = ++ redfish::EventServiceManager::getInstance().addSubscription(subValue, ++ false); ++ if (id.empty()) ++ { ++ messages::internalError(res); ++ res.end(); ++ return false; ++ } ++ ++ return true; ++} ++ ++static void deleteSubscription(std::shared_ptr<crow::SseConnection>& conn) ++{ ++ redfish::EventServiceManager::getInstance().deleteSubscription(conn); ++} ++ ++inline void requestRoutes(App& app) ++{ ++ BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/SSE") ++ .privileges({{"ConfigureComponents", "ConfigureManager"}}) ++ .serverSentEvent() ++ .onopen([](std::shared_ptr<crow::SseConnection>& conn, ++ const crow::Request& req, crow::Response& res) { ++ BMCWEB_LOG_DEBUG << "Connection " << conn << " opened."; ++ if (createSubscription(conn, req, res)) ++ { ++ // All success, lets send SSE haader ++ conn->sendSSEHeader(); ++ } ++ }) ++ .onclose([](std::shared_ptr<crow::SseConnection>& conn) { ++ BMCWEB_LOG_DEBUG << "Connection " << conn << " closed"; ++ deleteSubscription(conn); ++ }); ++} ++} // namespace eventservice_sse ++} // namespace redfish +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 3f398d7..dd833ce 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -22,15 +22,17 @@ + #include <sys/inotify.h> + + #include <boost/asio/io_context.hpp> ++#include <boost/beast/core/span.hpp> + #include <boost/container/flat_map.hpp> + #include <error_messages.hpp> + #include <event_service_store.hpp> + #include <http_client.hpp> + #include <persistent_data.hpp> + #include <random.hpp> +-#include <server_sent_events.hpp> ++#include <server_sent_event.hpp> + #include <utils/json_utils.hpp> + ++#include <algorithm> + #include <cstdlib> + #include <ctime> + #include <fstream> +@@ -46,9 +48,27 @@ using ReadingsObjType = + static constexpr const char* eventFormatType = "Event"; + static constexpr const char* metricReportFormatType = "MetricReport"; + ++static constexpr const char* subscriptionTypeSSE = "SSE"; + static constexpr const char* eventServiceFile = + "/var/lib/bmcweb/eventservice_config.json"; + ++static constexpr const uint8_t maxNoOfSubscriptions = 20; ++static constexpr const uint8_t maxNoOfSSESubscriptions = 10; ++ ++#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES ++static std::optional<boost::asio::posix::stream_descriptor> inotifyConn; ++static constexpr const char* redfishEventLogDir = "/var/log"; ++static constexpr const char* redfishEventLogFile = "/var/log/redfish"; ++static constexpr const size_t iEventSize = sizeof(inotify_event); ++static int inotifyFd = -1; ++static int dirWatchDesc = -1; ++static int fileWatchDesc = -1; ++ ++// <ID, timestamp, RedfishLogId, registryPrefix, MessageId, MessageArgs> ++using EventLogObjectsType = ++ std::tuple<std::string, std::string, std::string, std::string, std::string, ++ std::vector<std::string>>; ++ + namespace message_registries + { + inline boost::beast::span<const MessageEntry> +@@ -68,24 +88,6 @@ inline boost::beast::span<const MessageEntry> + } + return boost::beast::span<const MessageEntry>(openbmc::registry); + } +-} // namespace message_registries +- +-#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES +-static std::optional<boost::asio::posix::stream_descriptor> inotifyConn; +-static constexpr const char* redfishEventLogDir = "/var/log"; +-static constexpr const char* redfishEventLogFile = "/var/log/redfish"; +-static constexpr const size_t iEventSize = sizeof(inotify_event); +-static int inotifyFd = -1; +-static int dirWatchDesc = -1; +-static int fileWatchDesc = -1; +- +-// <ID, timestamp, RedfishLogId, registryPrefix, MessageId, MessageArgs> +-using EventLogObjectsType = +- std::tuple<std::string, std::string, std::string, std::string, std::string, +- std::vector<std::string>>; +- +-namespace message_registries +-{ + static const Message* + getMsgFromRegistry(const std::string& messageKey, + const boost::beast::span<const MessageEntry>& registry) +@@ -388,11 +390,9 @@ class Subscription : public persistent_data::UserSubscription + path, uriProto); + } + +- Subscription(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) : +- eventSeqNum(1) +- { +- sseConn = std::make_shared<crow::ServerSentEvents>(adaptor); +- } ++ Subscription(const std::shared_ptr<crow::SseConnection>& adaptor) : ++ sseConn(adaptor), eventSeqNum(1) ++ {} + + ~Subscription() = default; + +@@ -417,7 +417,7 @@ class Subscription : public persistent_data::UserSubscription + + if (sseConn != nullptr) + { +- sseConn->sendData(eventSeqNum, msg); ++ sseConn->sendEvent(std::to_string(eventSeqNum), msg); + } + } + +@@ -508,6 +508,7 @@ class Subscription : public persistent_data::UserSubscription + + this->sendEvent( + msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace)); ++ this->eventSeqNum++; + } + #endif + +@@ -578,14 +579,39 @@ class Subscription : public persistent_data::UserSubscription + return eventSeqNum; + } + ++ void setSubscriptionId(const std::string& id) ++ { ++ BMCWEB_LOG_DEBUG << "Subscription ID: " << id; ++ subId = id; ++ } ++ ++ std::string getSubscriptionId() ++ { ++ return subId; ++ } ++ ++ std::optional<std::string> ++ getSubscriptionId(const std::shared_ptr<crow::SseConnection>& connPtr) ++ { ++ if (sseConn != nullptr && connPtr == sseConn) ++ { ++ BMCWEB_LOG_DEBUG << __FUNCTION__ ++ << " conn matched, subId: " << subId; ++ return subId; ++ } ++ ++ return std::nullopt; ++ } ++ + private: ++ std::shared_ptr<crow::SseConnection> sseConn = nullptr; + uint64_t eventSeqNum; + std::string host; + std::string port; + std::string path; + std::string uriProto; + std::shared_ptr<crow::HttpClient> conn = nullptr; +- std::shared_ptr<crow::ServerSentEvents> sseConn = nullptr; ++ std::string subId; + }; + + class EventServiceManager +@@ -942,6 +968,8 @@ class EventServiceManager + subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); + subValue->updateRetryPolicy(); + ++ // Set Subscription ID for back trace ++ subValue->setSubscriptionId(id); + return id; + } + +@@ -970,11 +998,40 @@ class EventServiceManager + } + } + ++ void deleteSubscription(const std::shared_ptr<crow::SseConnection>& connPtr) ++ { ++ for (const auto& it : this->subscriptionsMap) ++ { ++ std::shared_ptr<Subscription> entry = it.second; ++ if (entry->subscriptionType == subscriptionTypeSSE) ++ { ++ std::optional<std::string> id = ++ entry->getSubscriptionId(connPtr); ++ if (id) ++ { ++ deleteSubscription(*id); ++ return; ++ } ++ } ++ } ++ } ++ + size_t getNumberOfSubscriptions() + { + return subscriptionsMap.size(); + } + ++ size_t getNumberOfSSESubscriptions() const ++ { ++ auto count = std::count_if( ++ subscriptionsMap.begin(), subscriptionsMap.end(), ++ [this](const std::pair<std::string, std::shared_ptr<Subscription>>& ++ entry) { ++ return (entry.second->subscriptionType == subscriptionTypeSSE); ++ }); ++ return static_cast<size_t>(count); ++ } ++ + std::vector<std::string> getAllIDs() + { + std::vector<std::string> idList; +diff --git a/redfish-core/include/server_sent_events.hpp b/redfish-core/include/server_sent_events.hpp +deleted file mode 100644 +index 7613d7b..0000000 +--- a/redfish-core/include/server_sent_events.hpp ++++ /dev/null +@@ -1,290 +0,0 @@ +- +-/* +-// Copyright (c) 2020 Intel Corporation +-// +-// Licensed under the Apache License, Version 2.0 (the "License"); +-// you may not use this file except in compliance with the License. +-// You may obtain a copy of the License at +-// +-// http://www.apache.org/licenses/LICENSE-2.0 +-// +-// Unless required by applicable law or agreed to in writing, software +-// distributed under the License is distributed on an "AS IS" BASIS, +-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-// See the License for the specific language governing permissions and +-// limitations under the License. +-*/ +-#pragma once +- +-#include <boost/asio/strand.hpp> +-#include <boost/beast/core/span.hpp> +-#include <boost/beast/http/buffer_body.hpp> +-#include <boost/beast/http/message.hpp> +-#include <boost/beast/version.hpp> +- +-#include <cstdlib> +-#include <functional> +-#include <iostream> +-#include <memory> +-#include <queue> +-#include <string> +- +-namespace crow +-{ +- +-static constexpr uint8_t maxReqQueueSize = 50; +- +-enum class SseConnState +-{ +- startInit, +- initInProgress, +- initialized, +- initFailed, +- sendInProgress, +- sendFailed, +- idle, +- suspended, +- closed +-}; +- +-class ServerSentEvents : public std::enable_shared_from_this<ServerSentEvents> +-{ +- private: +- std::shared_ptr<boost::beast::tcp_stream> sseConn; +- std::queue<std::pair<uint64_t, std::string>> requestDataQueue; +- std::string outBuffer; +- SseConnState state; +- int retryCount; +- int maxRetryAttempts; +- +- void sendEvent(const std::string& id, const std::string& msg) +- { +- if (msg.empty()) +- { +- BMCWEB_LOG_DEBUG << "Empty data, bailing out."; +- return; +- } +- +- if (state == SseConnState::sendInProgress) +- { +- return; +- } +- state = SseConnState::sendInProgress; +- +- if (!id.empty()) +- { +- outBuffer += "id: "; +- outBuffer.append(id.begin(), id.end()); +- outBuffer += "\n"; +- } +- +- outBuffer += "data: "; +- for (char character : msg) +- { +- outBuffer += character; +- if (character == '\n') +- { +- outBuffer += "data: "; +- } +- } +- outBuffer += "\n\n"; +- +- doWrite(); +- } +- +- void doWrite() +- { +- if (outBuffer.empty()) +- { +- BMCWEB_LOG_DEBUG << "All data sent successfully."; +- // Send is successful, Lets remove data from queue +- // check for next request data in queue. +- requestDataQueue.pop(); +- state = SseConnState::idle; +- checkQueue(); +- return; +- } +- +- sseConn->async_write_some( +- boost::asio::buffer(outBuffer.data(), outBuffer.size()), +- [self(shared_from_this())]( +- boost::beast::error_code ec, +- [[maybe_unused]] const std::size_t& bytesTransferred) { +- self->outBuffer.erase(0, bytesTransferred); +- +- if (ec == boost::asio::error::eof) +- { +- // Send is successful, Lets remove data from queue +- // check for next request data in queue. +- self->requestDataQueue.pop(); +- self->state = SseConnState::idle; +- self->checkQueue(); +- return; +- } +- +- if (ec) +- { +- BMCWEB_LOG_ERROR << "async_write_some() failed: " +- << ec.message(); +- self->state = SseConnState::sendFailed; +- self->checkQueue(); +- return; +- } +- BMCWEB_LOG_DEBUG << "async_write_some() bytes transferred: " +- << bytesTransferred; +- +- self->doWrite(); +- }); +- } +- +- void startSSE() +- { +- if (state == SseConnState::initInProgress) +- { +- return; +- } +- state = SseConnState::initInProgress; +- +- BMCWEB_LOG_DEBUG << "starting SSE connection "; +- using BodyType = boost::beast::http::buffer_body; +- auto response = +- std::make_shared<boost::beast::http::response<BodyType>>( +- boost::beast::http::status::ok, 11); +- auto serializer = +- std::make_shared<boost::beast::http::response_serializer<BodyType>>( +- *response); +- +- // TODO: Add hostname in http header. +- response->set(boost::beast::http::field::server, "iBMC"); +- response->set(boost::beast::http::field::content_type, +- "text/event-stream"); +- response->body().data = nullptr; +- response->body().size = 0; +- response->body().more = true; +- +- boost::beast::http::async_write_header( +- *sseConn, *serializer, +- [this, response, +- serializer](const boost::beast::error_code& ec, +- [[maybe_unused]] const std::size_t& bytesTransferred) { +- if (ec) +- { +- BMCWEB_LOG_ERROR << "Error sending header" << ec; +- state = SseConnState::initFailed; +- checkQueue(); +- return; +- } +- +- BMCWEB_LOG_DEBUG << "startSSE Header sent."; +- state = SseConnState::initialized; +- checkQueue(); +- }); +- } +- +- void checkQueue(const bool newRecord = false) +- { +- if (requestDataQueue.empty()) +- { +- BMCWEB_LOG_DEBUG << "requestDataQueue is empty\n"; +- return; +- } +- +- if (retryCount >= maxRetryAttempts) +- { +- BMCWEB_LOG_ERROR << "Maximum number of retries is reached."; +- +- // Clear queue. +- while (!requestDataQueue.empty()) +- { +- requestDataQueue.pop(); +- } +- +- // TODO: Take 'DeliveryRetryPolicy' action. +- // For now, doing 'SuspendRetries' action. +- state = SseConnState::suspended; +- return; +- } +- +- if ((state == SseConnState::initFailed) || +- (state == SseConnState::sendFailed)) +- { +- if (newRecord) +- { +- // We are already running async wait and retry. +- // Since record is added to queue, it gets the +- // turn in FIFO. +- return; +- } +- +- retryCount++; +- // TODO: Perform async wait for retryTimeoutInterval before proceed. +- } +- else +- { +- // reset retry count. +- retryCount = 0; +- } +- +- switch (state) +- { +- case SseConnState::initInProgress: +- case SseConnState::sendInProgress: +- case SseConnState::suspended: +- case SseConnState::startInit: +- case SseConnState::closed: +- // do nothing +- break; +- case SseConnState::initFailed: +- { +- startSSE(); +- break; +- } +- case SseConnState::initialized: +- case SseConnState::idle: +- case SseConnState::sendFailed: +- { +- std::pair<uint64_t, std::string> reqData = +- requestDataQueue.front(); +- sendEvent(std::to_string(reqData.first), reqData.second); +- break; +- } +- } +- +- return; +- } +- +- public: +- ServerSentEvents(const ServerSentEvents&) = delete; +- ServerSentEvents& operator=(const ServerSentEvents&) = delete; +- ServerSentEvents(ServerSentEvents&&) = delete; +- ServerSentEvents& operator=(ServerSentEvents&&) = delete; +- +- ServerSentEvents(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) : +- sseConn(adaptor), state(SseConnState::startInit), retryCount(0), +- maxRetryAttempts(5) +- { +- startSSE(); +- } +- +- ~ServerSentEvents() = default; +- +- void sendData(const uint64_t& id, const std::string& data) +- { +- if (state == SseConnState::suspended) +- { +- return; +- } +- +- if (requestDataQueue.size() <= maxReqQueueSize) +- { +- requestDataQueue.push(std::pair(id, data)); +- checkQueue(true); +- } +- else +- { +- BMCWEB_LOG_ERROR << "Request queue is full. So ignoring data."; +- } +- } +-}; +- +-} // namespace crow +diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp +index 8609862..249e594 100644 +--- a/redfish-core/lib/event_service.hpp ++++ b/redfish-core/lib/event_service.hpp +@@ -37,8 +37,6 @@ static constexpr const std::array<const char*, 1> supportedResourceTypes = { + "Task"}; + #endif + +-static constexpr const uint8_t maxNoOfSubscriptions = 20; +- + inline void requestRoutesEventService(App& app) + { + BMCWEB_ROUTE(app, "/redfish/v1/EventService/") +@@ -50,6 +48,8 @@ inline void requestRoutesEventService(App& app) + {"@odata.type", "#EventService.v1_5_0.EventService"}, + {"Id", "EventService"}, + {"Name", "Event Service"}, ++ {"ServerSentEventUri", ++ "/redfish/v1/EventService/Subscriptions/SSE"}, + {"Subscriptions", + {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}}, + {"Actions", +@@ -90,9 +90,7 @@ inline void requestRoutesEventService(App& app) + .privileges(redfish::privileges::patchEventService) + .methods(boost::beast::http::verb::patch)( + [](const crow::Request& req, +- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) +- +- { ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { + std::optional<bool> serviceEnabled; + std::optional<uint32_t> retryAttemps; + std::optional<uint32_t> retryInterval; +diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp +index bf98aae..53745d8 100644 +--- a/src/webserver_main.cpp ++++ b/src/webserver_main.cpp +@@ -6,6 +6,7 @@ + #include <cors_preflight.hpp> + #include <dbus_monitor.hpp> + #include <dbus_singleton.hpp> ++#include <eventservice_sse.hpp> + #include <google/google_service_root.hpp> + #include <hostname_monitor.hpp> + #include <ibm/management_console_rest.hpp> +@@ -83,6 +84,7 @@ int main(int /*argc*/, char** /*argv*/) + #endif + + #ifdef BMCWEB_ENABLE_REDFISH ++ redfish::eventservice_sse::requestRoutes(app); + redfish::requestRoutes(app); + redfish::RedfishService redfish(app); + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch new file mode 100644 index 000000000..3914cc81a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch @@ -0,0 +1,296 @@ +From 769f0e20d0a7e786d7091ffb7ee57d35204dfa28 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Wed, 17 Mar 2021 01:16:50 +0000 +Subject: [PATCH] Add EventService SSE filter support + +This commit implements the Event Service SSE stream +filters support. As per redfish specification: +The SSE streams have these formats: + - Metric report SSE stream + - Event message SSE stream + +To reduce the amount of data, service supports $filter +query parameter in SSE URI. +Below properties support as filter criteria: + - EventFormatType( Event & MetricReport) + - MessageId + - RegistryPrefix + - MetricReportDefinition + +For more details, refer Redfish specification section 13.5.2 + +Tested: + Created SSE stream with different filters and observed + desired events on SSE stream client(browser), some examples + - To get all Redfish events, + URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event) + - To get Redfish events with RegistryPrefix "OpenBMC" + URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(RegistryPrefix%20eq%20OpenBMC) + - To get only DC power of Events, + URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event)%20and%20(MessageId%20eq%20DCPowerOff) + +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +Change-Id: I55c6f53bb5e57aa1f2d1601f1a16525a33b13bd2 +--- + include/eventservice_sse.hpp | 145 +++++++++++++++++- + redfish-core/include/error_messages.hpp | 9 ++ + .../include/event_service_manager.hpp | 5 + + redfish-core/lib/event_service.hpp | 5 - + redfish-core/src/error_messages.cpp | 26 ++++ + 5 files changed, 181 insertions(+), 9 deletions(-) + +diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp +index 14daf00..fed7fec 100644 +--- a/include/eventservice_sse.hpp ++++ b/include/eventservice_sse.hpp +@@ -23,16 +23,153 @@ static bool createSubscription(std::shared_ptr<crow::SseConnection>& conn, + } + BMCWEB_LOG_DEBUG << "Request query param size: " << req.urlParams.size(); + ++ // EventService SSE supports only "$filter" query param. ++ if (req.urlParams.size() > 1) ++ { ++ messages::invalidQueryFilter(res); ++ res.end(); ++ return false; ++ } ++ std::string eventFormatType; ++ std::string queryFilters; ++ if (req.urlParams.size()) ++ { ++ boost::urls::url_view::params_type::iterator it = ++ req.urlParams.find("$filter"); ++ if (it == req.urlParams.end()) ++ { ++ messages::invalidQueryFilter(res); ++ res.end(); ++ return false; ++ } ++ queryFilters = it->value(); ++ } ++ else ++ { ++ eventFormatType = "Event"; ++ } ++ ++ std::vector<std::string> msgIds; ++ std::vector<std::string> regPrefixes; ++ std::vector<std::string> mrdsArray; ++ if (!queryFilters.empty()) ++ { ++ // Reading from query params. ++ bool status = readSSEQueryParams(queryFilters, eventFormatType, msgIds, ++ regPrefixes, mrdsArray); ++ if (!status) ++ { ++ messages::invalidObject(res, queryFilters); ++ res.end(); ++ return false; ++ } ++ ++ // RegsitryPrefix and messageIds are mutuly exclusive as per redfish ++ // specification. ++ if (regPrefixes.size() && msgIds.size()) ++ { ++ messages::mutualExclusiveProperties(res, "RegistryPrefix", ++ "MessageId"); ++ res.end(); ++ return false; ++ } ++ ++ if (!eventFormatType.empty()) ++ { ++ if (std::find(supportedEvtFormatTypes.begin(), ++ supportedEvtFormatTypes.end(), ++ eventFormatType) == supportedEvtFormatTypes.end()) ++ { ++ messages::propertyValueNotInList(res, eventFormatType, ++ "EventFormatType"); ++ res.end(); ++ return false; ++ } ++ } ++ else ++ { ++ // If nothing specified, using default "Event" ++ eventFormatType = "Event"; ++ } ++ ++ if (!regPrefixes.empty()) ++ { ++ for (const std::string& it : regPrefixes) ++ { ++ if (std::find(supportedRegPrefixes.begin(), ++ supportedRegPrefixes.end(), ++ it) == supportedRegPrefixes.end()) ++ { ++ messages::propertyValueNotInList(res, it, "RegistryPrefix"); ++ res.end(); ++ return false; ++ } ++ } ++ } ++ ++ if (!msgIds.empty()) ++ { ++ std::vector<std::string> registryPrefix; ++ ++ // If no registry prefixes are mentioned, consider all supported ++ // prefixes to validate message ID ++ if (regPrefixes.empty()) ++ { ++ registryPrefix.assign(supportedRegPrefixes.begin(), ++ supportedRegPrefixes.end()); ++ } ++ else ++ { ++ registryPrefix = regPrefixes; ++ } ++ ++ for (const std::string& id : msgIds) ++ { ++ bool validId = false; ++ ++ // Check for Message ID in each of the selected Registry ++ for (const std::string& it : registryPrefix) ++ { ++ const boost::beast::span< ++ const redfish::message_registries::MessageEntry> ++ registry = ++ redfish::message_registries::getRegistryFromPrefix( ++ it); ++ ++ if (std::any_of( ++ registry.cbegin(), registry.cend(), ++ [&id]( ++ const redfish::message_registries::MessageEntry& ++ messageEntry) { ++ return !id.compare(messageEntry.first); ++ })) ++ { ++ validId = true; ++ break; ++ } ++ } ++ ++ if (!validId) ++ { ++ messages::propertyValueNotInList(res, id, "MessageIds"); ++ res.end(); ++ return false; ++ } ++ } ++ } ++ } ++ + std::shared_ptr<redfish::Subscription> subValue = + std::make_shared<redfish::Subscription>(std::move(conn)); + + // GET on this URI means, Its SSE subscriptionType. +- subValue->subscriptionType = redfish::subscriptionTypeSSE; +- +- // TODO: parse $filter query params and fill config. ++ subValue->subscriptionType = subscriptionTypeSSE; + subValue->protocol = "Redfish"; + subValue->retryPolicy = "TerminateAfterRetries"; +- subValue->eventFormatType = "Event"; ++ subValue->eventFormatType = eventFormatType; ++ subValue->registryMsgIds = msgIds; ++ subValue->registryPrefixes = regPrefixes; ++ subValue->metricReportDefinitions = mrdsArray; + + std::string id = + redfish::EventServiceManager::getInstance().addSubscription(subValue, +diff --git a/redfish-core/include/error_messages.hpp b/redfish-core/include/error_messages.hpp +index 3d11cc4..90084e3 100644 +--- a/redfish-core/include/error_messages.hpp ++++ b/redfish-core/include/error_messages.hpp +@@ -971,6 +971,15 @@ nlohmann::json mutualExclusiveProperties(const std::string& arg1, + void mutualExclusiveProperties(crow::Response& res, const std::string& arg1, + const std::string& arg2); + ++/** ++ * @brief Formats InvalidQueryFilter message into JSON ++ * Message body: "The requested URL contains the invalid query filters" ++ * ++ * @returns Message InvalidQueryFilter formatted to JSON */ ++nlohmann::json invalidQueryFilter(); ++ ++void invalidQueryFilter(crow::Response& res); ++ + } // namespace messages + + } // namespace redfish +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index dd833ce..861f4cb 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -55,6 +55,11 @@ static constexpr const char* eventServiceFile = + static constexpr const uint8_t maxNoOfSubscriptions = 20; + static constexpr const uint8_t maxNoOfSSESubscriptions = 10; + ++static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = { ++ eventFormatType, metricReportFormatType}; ++static constexpr const std::array<const char*, 2> supportedRegPrefixes = { ++ "OpenBMC", "TaskEvent"}; ++ + #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES + static std::optional<boost::asio::posix::stream_descriptor> inotifyConn; + static constexpr const char* redfishEventLogDir = "/var/log"; +diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp +index 249e594..6f01707 100644 +--- a/redfish-core/lib/event_service.hpp ++++ b/redfish-core/lib/event_service.hpp +@@ -21,11 +21,6 @@ + + namespace redfish + { +- +-static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = { +- eventFormatType, metricReportFormatType}; +-static constexpr const std::array<const char*, 3> supportedRegPrefixes = { +- "Base", "OpenBMC", "TaskEvent"}; + static constexpr const std::array<const char*, 3> supportedRetryPolicies = { + "TerminateAfterRetries", "SuspendRetries", "RetryForever"}; + +diff --git a/redfish-core/src/error_messages.cpp b/redfish-core/src/error_messages.cpp +index 9c28e8f..2394398 100644 +--- a/redfish-core/src/error_messages.cpp ++++ b/redfish-core/src/error_messages.cpp +@@ -2173,6 +2173,32 @@ void mutualExclusiveProperties(crow::Response& res, const std::string& arg1, + addMessageToErrorJson(res.jsonValue, mutualExclusiveProperties(arg1, arg2)); + } + ++/** ++ * @internal ++ * @brief Formats InvalidQueryFilter into JSON ++ * ++ * See header file for more information ++ * @endinternal ++ */ ++nlohmann::json invalidQueryFilter() ++{ ++ return nlohmann::json{ ++ {"@odata.type", "#Message.v1_0_0.Message"}, ++ {"MessageId", "Base.1.5.0.InvalidQueryFilter"}, ++ {"Message", "The requested url contains the invalid query filter."}, ++ {"MessageArgs", nlohmann::json::array()}, ++ {"Severity", "Warning"}, ++ {"Resolution", ++ "Ensure the correct query filter is specified in requested url " ++ "and resubmit the request."}}; ++} ++ ++void invalidQueryFilter(crow::Response& res) ++{ ++ res.result(boost::beast::http::status::bad_request); ++ addMessageToErrorJson(res.jsonValue, invalidQueryFilter()); ++} ++ + } // namespace messages + + } // namespace redfish +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0007-EventService-Log-events-for-subscription-actions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0007-EventService-Log-events-for-subscription-actions.patch new file mode 100644 index 000000000..3be65ee2a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0007-EventService-Log-events-for-subscription-actions.patch @@ -0,0 +1,132 @@ +From b8eb53886106e44e3668857b13f8642d2ad3cfbf Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Fri, 27 Aug 2021 16:02:01 +0000 +Subject: [PATCH] EventService: Log events for subscription actions + +Log redfish event for below 3 actions + - Add new subscription + - Update existing subscription properties + - Delete existing subscription +in order to notify the subscribed clients on the subscription related +information. + +Modified method name accordingly to indicate the clear purpose and +added updateSubscription method with subscription id param +to log event for subscription update. + +Tested: + - Performed all the above actions and verified the redfish event + messages are logged. + +Change-Id: I3745fa6357bd215379781a9818d9acc02a853d79 +Signed-off-by: AppaRao Puli <apparao.puli@intel.com> +Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com> +--- + .../include/event_service_manager.hpp | 35 ++++++++++++++++--- + redfish-core/lib/event_service.hpp | 2 +- + 2 files changed, 32 insertions(+), 5 deletions(-) + +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index c3e7f61..e9bdbfa 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -21,6 +21,7 @@ + #include "registries/task_event_message_registry.hpp" + + #include <sys/inotify.h> ++#include <systemd/sd-journal.h> + + #include <boost/asio/io_context.hpp> + #include <boost/beast/core/span.hpp> +@@ -788,7 +789,7 @@ class EventServiceManager + } + } + +- void updateSubscriptionData() ++ void persistSubscriptionData() + { + persistent_data::EventServiceStore::getInstance() + .eventServiceConfig.enabled = serviceEnabled; +@@ -835,7 +836,7 @@ class EventServiceManager + + if (updateConfig) + { +- updateSubscriptionData(); ++ persistSubscriptionData(); + } + + if (updateRetryCfg) +@@ -947,7 +948,7 @@ class EventServiceManager + + if (updateFile) + { +- updateSubscriptionData(); ++ persistSubscriptionData(); + } + + #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES +@@ -962,6 +963,13 @@ class EventServiceManager + + // Set Subscription ID for back trace + subValue->setSubscriptionId(id); ++ ++ /* Log event for subscription addition */ ++ sd_journal_send("MESSAGE=Event subscription added(Id: %s)", id.c_str(), ++ "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s", ++ "OpenBMC.0.1.EventSubscriptionAdded", ++ "REDFISH_MESSAGE_ARGS=%s", id.c_str(), NULL); ++ + return id; + } + +@@ -986,7 +994,14 @@ class EventServiceManager + persistent_data::EventServiceStore::getInstance() + .subscriptionsConfigMap.erase(obj2); + updateNoOfSubscribersCount(); +- updateSubscriptionData(); ++ ++ persistSubscriptionData(); ++ /* Log event for subscription delete. */ ++ sd_journal_send("MESSAGE=Event subscription removed.(Id = %s)", ++ id.c_str(), "PRIORITY=%i", LOG_INFO, ++ "REDFISH_MESSAGE_ID=%s", ++ "OpenBMC.0.1.EventSubscriptionRemoved", ++ "REDFISH_MESSAGE_ARGS=%s", id.c_str(), NULL); + } + } + +@@ -1008,6 +1023,18 @@ class EventServiceManager + } + } + ++ void updateSubscription(const std::string& id) ++ { ++ persistSubscriptionData(); ++ ++ /* Log event for subscription update. */ ++ sd_journal_send("MESSAGE=Event subscription updated.(Id = %s)", ++ id.c_str(), "PRIORITY=%i", LOG_INFO, ++ "REDFISH_MESSAGE_ID=%s", ++ "OpenBMC.0.1.EventSubscriptionUpdated", ++ "REDFISH_MESSAGE_ARGS=%s", id.c_str(), NULL); ++ } ++ + size_t getNumberOfSubscriptions() + { + return subscriptionsMap.size(); +diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp +index 9def549..6a8421f 100644 +--- a/redfish-core/lib/event_service.hpp ++++ b/redfish-core/lib/event_service.hpp +@@ -617,7 +617,7 @@ inline void requestRoutesEventDestination(App& app) + subValue->updateRetryPolicy(); + } + +- EventServiceManager::getInstance().updateSubscriptionData(); ++ EventServiceManager::getInstance().updateSubscription(param); + }); + BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") + // The below privilege is wrong, it should be ConfigureManager OR +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch new file mode 100644 index 000000000..84ceb4ba8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch @@ -0,0 +1,85 @@ +From 05fdea2bb8e486b058d137a067ce1f5c885d2a96 Mon Sep 17 00:00:00 2001 +From: Nitin Wankhade <nitinx.arunrao.wankhade@intel.com> +Date: Mon, 28 Jun 2021 19:59:57 +0000 +Subject: [PATCH] Add checks on Event Subscription input parameters + +There is no check on the size of input parameters(Context, +Destination and Header) during Event Subscription.This +creates out of memory situation. +This commit checks for the size of input parameters and +rejects if it is exceeding the input size limits. + +Tested + - Validated using POST on Event Subscription. + - When Context, Destination and Headers were too long, + received a error message denoting the same. + +Change-Id: Iec2cd766c0e137b72706fc2da468d4fefd8fbaae +Signed-off-by: Nitin Wankhade <nitinx.arunrao.wankhade@intel.com> +--- + redfish-core/lib/event_service.hpp | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp +index 52b01e5..f8a1671 100644 +--- a/redfish-core/lib/event_service.hpp ++++ b/redfish-core/lib/event_service.hpp +@@ -19,6 +19,10 @@ + #include <app.hpp> + #include <registries/privilege_registry.hpp> + ++#define MAX_CONTEXT_SIZE 256 ++#define MAX_DESTINATION_SIZE 1024 ++#define MAX_HEADER_SIZE 8096 ++ + namespace redfish + { + static constexpr const std::array<const char*, 3> supportedRetryPolicies = { +@@ -220,6 +224,12 @@ inline void requestRoutesEventDestinationCollection(App& app) + return; + } + ++ if (destUrl.size() > MAX_DESTINATION_SIZE) ++ { ++ messages::propertySizeExceeded(asyncResp->res, "Destination"); ++ return; ++ } ++ + if (regPrefixes && msgIds) + { + if (regPrefixes->size() && msgIds->size()) +@@ -330,11 +340,31 @@ inline void requestRoutesEventDestinationCollection(App& app) + + if (context) + { ++ if (context->size() > MAX_CONTEXT_SIZE) ++ { ++ messages::propertySizeExceeded(asyncResp->res, "Context"); ++ return; ++ } + subValue->customText = *context; + } + + if (headers) + { ++ size_t cumulativeLen = 0; ++ ++ for (nlohmann::json& itr : *headers) ++ { ++ std::string hdr{itr.dump( ++ -1, ' ', true, nlohmann::json::error_handler_t::replace)}; ++ cumulativeLen += hdr.length(); ++ ++ if (cumulativeLen > MAX_HEADER_SIZE) ++ { ++ messages::propertySizeExceeded(asyncResp->res, ++ "HttpHeaders"); ++ return; ++ } ++ } + subValue->httpHeaders = *headers; + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0009-Restructure-Redifsh-EventLog-Transmit-code-flow.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0009-Restructure-Redifsh-EventLog-Transmit-code-flow.patch new file mode 100644 index 000000000..d1fe475f5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0009-Restructure-Redifsh-EventLog-Transmit-code-flow.patch @@ -0,0 +1,225 @@ +From 542505dff60e3921b00b51acae882e207d46f1a6 Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Wed, 14 Jul 2021 14:13:11 +0000 +Subject: [PATCH] Restructure Redfish EventLog Transmit code flow + +In the current implementation: + 1. When Event service is disabled and enabled back after a while, + all the logs during this time span between disable to enable + are dumped to the Event listener. + 2. When two events occur very close (in terms of microseconds) + and they trigger two different iNotify events, the listener + receives both of these events with the same Event ID. + +This occurs as the last log time stamp read from redfish file +and previous time stamp used to generate Event ID's are not +being updated continuously. + +This commit fixes this issue by tweaking the logic to continuously +update the time stamp values (even during when Event Service is +disabled), and also replaces multiple string operations with file +operations. i.e. Instead of looping through the entire Redfish file +until last time stamp read is reached, this fix makes use of +fseek to get to the last read position. + +Tested: + - Subscribed to an event and successfully received Event Logs. + - No Event Logs were received when Event Service was disabled. + - No Dump of past Events after Event Service was enabled. + - Redfish Validator passed + +Change-Id: I87136bee78076b1b3219930813702b3b9d20c157 +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + .../include/event_service_manager.hpp | 108 ++++++++++++------ + 1 file changed, 76 insertions(+), 32 deletions(-) + +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index e9bdbfa..5c4de70 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -134,15 +134,10 @@ static const Message* formatMessage(const std::string_view& messageID) + + namespace event_log + { +-inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID, +- const bool firstEntry = true) ++inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) + { + static time_t prevTs = 0; + static int index = 0; +- if (firstEntry) +- { +- prevTs = 0; +- } + + // Get the entry timestamp + std::time_t curTs = 0; +@@ -621,6 +616,7 @@ class EventServiceManager + } + + std::string lastEventTStr; ++ std::streampos redfishLogFilePosition{0}; + size_t noOfEventLogSubscribers{0}; + size_t noOfMetricReportSubscribers{0}; + std::shared_ptr<sdbusplus::bus::match::match> matchTelemetryMonitor; +@@ -1163,7 +1159,22 @@ class EventServiceManager + #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES + void cacheLastEventTimestamp() + { +- lastEventTStr.clear(); ++ // Control comes here when : ++ // 1. Subscription is added and lastEventTStr is empty ++ // 2. lastEventTStr is empty ++ // 3. When a new Redfish file is created ++ ++ if (!lastEventTStr.empty()) ++ { ++ // Control would be here when Redfish file is created. ++ // Reset File Position as new file is created ++ redfishLogFilePosition = 0; ++ return; ++ } ++ ++ // Open the redfish file and read till the last record to get the ++ // last event's time stamp. ++ + std::ifstream logStream(redfishEventLogFile); + if (!logStream.good()) + { +@@ -1171,27 +1182,44 @@ class EventServiceManager + return; + } + std::string logEntry; ++ std::string prev_logEntry; + while (std::getline(logStream, logEntry)) + { +- size_t space = logEntry.find_first_of(' '); +- if (space == std::string::npos) +- { +- // Shouldn't enter here but lets skip it. +- BMCWEB_LOG_DEBUG << "Invalid log entry found."; +- continue; +- } +- lastEventTStr = logEntry.substr(0, space); ++ prev_logEntry = logEntry; ++ redfishLogFilePosition = logStream.tellg(); ++ } ++ ++ if (prev_logEntry.empty()) ++ { ++ BMCWEB_LOG_ERROR ++ << "Last Event Time Stamp Caching Failed : No Records"; ++ redfishLogFilePosition = 0; ++ return; ++ } ++ ++ size_t space = prev_logEntry.find_first_of(' '); ++ if (space == std::string::npos) ++ { ++ // Shouldn't enter here but lets skip it. ++ BMCWEB_LOG_DEBUG << "Invalid log entry found."; ++ BMCWEB_LOG_ERROR << "Last Event Time Stamp Caching Failed"; ++ return; + } ++ lastEventTStr = prev_logEntry.substr(0, space); + BMCWEB_LOG_DEBUG << "Last Event time stamp set: " << lastEventTStr; ++ BMCWEB_LOG_DEBUG << "Next Log Position : " << redfishLogFilePosition; + } + + void readEventLogsFromFile() + { +- if (!serviceEnabled || !noOfEventLogSubscribers) ++ if (lastEventTStr.empty()) + { +- BMCWEB_LOG_DEBUG << "EventService disabled or no Subscriptions."; +- return; ++ // Shouldn't ideally enter here. ++ // Last event Time stamp would be set by now. ++ // Just incase of any failures before. ++ cacheLastEventTimestamp(); + } ++ + std::ifstream logStream(redfishEventLogFile); + if (!logStream.good()) + { +@@ -1201,27 +1229,21 @@ class EventServiceManager + + std::vector<EventLogObjectsType> eventRecords; + +- bool startLogCollection = false; +- bool firstEntry = true; +- + std::string logEntry; ++ ++ // Get the read pointer to the next log to be read. ++ logStream.seekg(redfishLogFilePosition); ++ + while (std::getline(logStream, logEntry)) + { +- if (!startLogCollection && !lastEventTStr.empty()) +- { +- if (boost::starts_with(logEntry, lastEventTStr)) +- { +- startLogCollection = true; +- } +- continue; +- } ++ // Update Pointer position ++ redfishLogFilePosition = logStream.tellg(); + + std::string idStr; +- if (!event_log::getUniqueEntryID(logEntry, idStr, firstEntry)) ++ if (!event_log::getUniqueEntryID(logEntry, idStr)) + { + continue; + } +- firstEntry = false; + + std::string timestamp; + std::string messageID; +@@ -1233,6 +1255,16 @@ class EventServiceManager + continue; + } + ++ lastEventTStr = timestamp; ++ ++ if (!serviceEnabled || !noOfEventLogSubscribers) ++ { ++ // If Service is not enabled, no need to compute ++ // the remaining items below. ++ // But, Loop must continue to keep track of Timestamp ++ continue; ++ } ++ + std::string registryName; + std::string messageKey; + event_log::getRegistryAndMessageKey(messageID, registryName, +@@ -1242,11 +1274,23 @@ class EventServiceManager + continue; + } + +- lastEventTStr = timestamp; + eventRecords.emplace_back(idStr, timestamp, messageID, registryName, + messageKey, messageArgs); + } + ++ if (!serviceEnabled || !noOfEventLogSubscribers) ++ { ++ BMCWEB_LOG_DEBUG << "EventService disabled or no Subscriptions."; ++ return; ++ } ++ ++ if (eventRecords.empty()) ++ { ++ // No Records to send ++ BMCWEB_LOG_DEBUG << "No log entries available to be transferred."; ++ return; ++ } ++ + for (const auto& it : this->subscriptionsMap) + { + std::shared_ptr<Subscription> entry = it.second; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0010-Remove-Terminated-Event-Subscriptions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0010-Remove-Terminated-Event-Subscriptions.patch new file mode 100644 index 000000000..9af5a066b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0010-Remove-Terminated-Event-Subscriptions.patch @@ -0,0 +1,258 @@ +From f665ba085bb2310f008b7534f827fb401ad973c2 Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Tue, 12 Oct 2021 08:19:51 +0000 +Subject: [PATCH] Delete/Remove Terminated Event Subscription(s) + +Added functionality to delete/remove event subscription(s) which are +configured to Terminate after retries. + +Currently, when an Event is subscribed with Retry Policy as +"TerminateAfterRetries", the state of the connection is set to +"Terminated" after retrying, but the Subscription is not removed. +This commit adds the functionality to detect terminated connection and +remove the respective subscription. + +Tested: + - Created a Subscription with + DeliveryRetryPolicy: "TerminateAfterRetries" + - Received Events successfully on Event listener + - Once the Event listener was stopped, the Subscription was + removed/deleted after retries. + +Change-Id: If447acb2db74fb29a5d1cfe6194b77cda82bc8a1 +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/http_client.hpp | 43 +++++++++++++++---- + .../include/event_service_manager.hpp | 36 ++++++++++++++++ + 2 files changed, 70 insertions(+), 9 deletions(-) + +diff --git a/http/http_client.hpp b/http/http_client.hpp +index 5e7ff47..54ae2c3 100644 +--- a/http/http_client.hpp ++++ b/http/http_client.hpp +@@ -55,6 +55,8 @@ enum class ConnState + closeInProgress, + closed, + suspended, ++ terminate, ++ terminateInProgress, + terminated, + abortConnection, + retry +@@ -288,7 +290,14 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + } + void doClose() + { +- state = ConnState::closeInProgress; ++ if (state == ConnState::terminate) ++ { ++ state = ConnState::terminateInProgress; ++ } ++ else if (state != ConnState::suspended) ++ { ++ state = ConnState::closeInProgress; ++ } + + // Set the timeout on the tcp stream socket for the async operation + conn.expires_after(std::chrono::seconds(30)); +@@ -318,8 +327,11 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + } + self->conn.close(); + +- if ((self->state != ConnState::suspended) && +- (self->state != ConnState::terminated)) ++ if (self->state == ConnState::terminateInProgress) ++ { ++ self->state = ConnState::terminated; ++ } ++ else if (self->state == ConnState::closeInProgress) + { + self->state = ConnState::closed; + self->handleConnState(); +@@ -341,8 +353,11 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + } + conn.close(); + +- if ((state != ConnState::suspended) && +- (state != ConnState::terminated)) ++ if (state == ConnState::terminateInProgress) ++ { ++ state = ConnState::terminated; ++ } ++ else if (state == ConnState::closeInProgress) + { + state = ConnState::closed; + handleConnState(); +@@ -365,8 +380,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + BMCWEB_LOG_DEBUG << "Retry policy: " << retryPolicyAction; + if (retryPolicyAction == "TerminateAfterRetries") + { +- // TODO: delete subscription +- state = ConnState::terminated; ++ state = ConnState::terminate; + } + if (retryPolicyAction == "SuspendRetries") + { +@@ -423,6 +437,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + case ConnState::sendInProgress: + case ConnState::recvInProgress: + case ConnState::closeInProgress: ++ case ConnState::terminateInProgress: + { + BMCWEB_LOG_DEBUG << "Async operation is already in progress"; + break; +@@ -439,7 +454,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + break; + } + case ConnState::suspended: +- case ConnState::terminated: ++ case ConnState::terminate: + { + doClose(); + break; +@@ -506,7 +521,8 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + } + void sendData(const std::string& data) + { +- if ((state == ConnState::suspended) || (state == ConnState::terminated)) ++ if ((state == ConnState::terminate) || ++ (state == ConnState::terminated) || (state == ConnState::suspended)) + { + return; + } +@@ -524,6 +540,15 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + return; + } + ++ bool isTerminated() ++ { ++ if (state == ConnState::terminated) ++ { ++ return true; ++ } ++ return false; ++ } ++ + void addHeaders( + const std::vector<std::pair<std::string, std::string>>& httpHeaders) + { +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 6f60a31..363adb0 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -591,6 +591,14 @@ class Subscription : public persistent_data::UserSubscription + return std::nullopt; + } + ++ bool isTerminated() ++ { ++ if (conn != nullptr) ++ return conn->isTerminated(); ++ ++ return false; ++ } ++ + private: + std::shared_ptr<crow::SseConnection> sseConn = nullptr; + uint64_t eventSeqNum; +@@ -847,6 +855,22 @@ class EventServiceManager + } + } + ++ void deleteTerminatedSubcriptions() ++ { ++ boost::container::flat_map<std::string, ++ std::shared_ptr<Subscription>>::iterator it = ++ subscriptionsMap.begin(); ++ while (it != subscriptionsMap.end()) ++ { ++ std::shared_ptr<Subscription> entry = it->second; ++ if (entry->isTerminated()) ++ { ++ subscriptionsMap.erase(it); ++ } ++ it++; ++ } ++ } ++ + void updateNoOfSubscribersCount() + { + size_t eventLogSubCount = 0; +@@ -881,6 +905,7 @@ class EventServiceManager + + std::shared_ptr<Subscription> getSubscription(const std::string& id) + { ++ deleteTerminatedSubcriptions(); + auto obj = subscriptionsMap.find(id); + if (obj == subscriptionsMap.end()) + { +@@ -971,6 +996,7 @@ class EventServiceManager + + bool isSubscriptionExist(const std::string& id) + { ++ deleteTerminatedSubcriptions(); + auto obj = subscriptionsMap.find(id); + if (obj == subscriptionsMap.end()) + { +@@ -1033,6 +1059,7 @@ class EventServiceManager + + size_t getNumberOfSubscriptions() + { ++ deleteTerminatedSubcriptions(); + return subscriptionsMap.size(); + } + +@@ -1049,6 +1076,7 @@ class EventServiceManager + + std::vector<std::string> getAllIDs() + { ++ deleteTerminatedSubcriptions(); + std::vector<std::string> idList; + for (const auto& it : subscriptionsMap) + { +@@ -1059,6 +1087,7 @@ class EventServiceManager + + bool isDestinationExist(const std::string& destUrl) + { ++ deleteTerminatedSubcriptions(); + for (const auto& it : subscriptionsMap) + { + std::shared_ptr<Subscription> entry = it.second; +@@ -1073,6 +1102,7 @@ class EventServiceManager + + void sendTestEventLog() + { ++ deleteTerminatedSubcriptions(); + for (const auto& it : this->subscriptionsMap) + { + std::shared_ptr<Subscription> entry = it.second; +@@ -1100,6 +1130,8 @@ class EventServiceManager + } + eventRecord.push_back(eventMessage); + ++ deleteTerminatedSubcriptions(); ++ + for (const auto& it : this->subscriptionsMap) + { + std::shared_ptr<Subscription> entry = it.second; +@@ -1143,6 +1175,8 @@ class EventServiceManager + } + void sendBroadcastMsg(const std::string& broadcastMsg) + { ++ deleteTerminatedSubcriptions(); ++ + for (const auto& it : this->subscriptionsMap) + { + std::shared_ptr<Subscription> entry = it.second; +@@ -1291,6 +1325,8 @@ class EventServiceManager + return; + } + ++ deleteTerminatedSubcriptions(); ++ + for (const auto& it : this->subscriptionsMap) + { + std::shared_ptr<Subscription> entry = it.second; +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch new file mode 100644 index 000000000..585f7bf09 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch @@ -0,0 +1,141 @@ +From 5b87bb61b58e92a8c5af37a7959347747409a65c Mon Sep 17 00:00:00 2001 +From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +Date: Thu, 14 Oct 2021 02:56:11 +0530 +Subject: [PATCH] Fix bmcweb crash while deleting terminated subscriptions + +This commit fixes bmcweb crash while deleting the terminated +subscriptions. In the earlier implementation, detection of subscription +to be deleted and the deletion(erase) was happening in the same loop. +Due to this, if the Subscription to be deleted is the last one in the +list, the loop will enter into infinite loop. The fix is to keep the +detection and deletion loop separate. +Also, this commit adds code to : + - Delete from persistent storage + - Add journal entry for deleted entry + - update number of subcribers and update persistent storage. + +Apart from this, this commit also moves the retry timer check to the top +to avoid multiple calls to close when the retry count is 3 and timer is +running. + +Tested: + - Checked journal logs to confirm each retry is actually spanned to be + 30 secs + - Verified Journal entry for deleted subscription after retires. + - Verified Event service functionality by making three subscriptions: + retry for ever, terminate after retires and suspend after retries. + +Change-Id: I425a6c749923ce86c457a36394deb0fbbee232db +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/http_client.hpp | 11 ++-- + .../include/event_service_manager.hpp | 59 ++++++++++++++++--- + 2 files changed, 58 insertions(+), 12 deletions(-) + +diff --git a/http/http_client.hpp b/http/http_client.hpp +index 54ae2c3..162cb09 100644 +--- a/http/http_client.hpp ++++ b/http/http_client.hpp +@@ -367,6 +367,12 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + + void waitAndRetry() + { ++ if (runningTimer) ++ { ++ BMCWEB_LOG_DEBUG << "Retry timer is already running."; ++ return; ++ } ++ + if (retryCount >= maxRetryAttempts) + { + BMCWEB_LOG_ERROR << "Maximum number of retries reached."; +@@ -393,11 +399,6 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + return; + } + +- if (runningTimer) +- { +- BMCWEB_LOG_DEBUG << "Retry timer is already running."; +- return; +- } + runningTimer = true; + + retryCount++; +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 363adb0..7af7a4d 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -857,18 +857,63 @@ class EventServiceManager + + void deleteTerminatedSubcriptions() + { +- boost::container::flat_map<std::string, +- std::shared_ptr<Subscription>>::iterator it = +- subscriptionsMap.begin(); +- while (it != subscriptionsMap.end()) ++ BMCWEB_LOG_ERROR << "Map size Before Delete : " ++ << subscriptionsMap.size(); ++ ++ std::vector<std::string> deleteIds; ++ ++ // Determine Subscription ID's to be deleted. ++ for (const auto& it : subscriptionsMap) + { +- std::shared_ptr<Subscription> entry = it->second; ++ std::shared_ptr<Subscription> entry = it.second; + if (entry->isTerminated()) + { +- subscriptionsMap.erase(it); ++ deleteIds.emplace_back(it.first); ++ } ++ } ++ ++ // Delete the Terminated Subcriptions ++ for (std::string& id : deleteIds) ++ { ++ auto map1 = subscriptionsMap.find(id); ++ if (map1 != subscriptionsMap.end()) ++ { ++ subscriptionsMap.erase(map1); ++ auto map2 = persistent_data::EventServiceStore::getInstance() ++ .subscriptionsConfigMap.find(id); ++ if (map2 != persistent_data::EventServiceStore::getInstance() ++ .subscriptionsConfigMap.end()) ++ { ++ persistent_data::EventServiceStore::getInstance() ++ .subscriptionsConfigMap.erase(map2); ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "Couldn't find ID: " << id ++ << " in subscriptionsConfigMap"; ++ } ++ ++ /* Log event for subscription delete. */ ++ sd_journal_send("MESSAGE=Event subscription removed.(Id = %s)", ++ id.c_str(), "PRIORITY=%i", LOG_INFO, ++ "REDFISH_MESSAGE_ID=%s", ++ "OpenBMC.0.1.EventSubscriptionRemoved", ++ "REDFISH_MESSAGE_ARGS=%s", id.c_str(), NULL); ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "Couldn't find ID: " << id ++ << " in subscriptionsMap"; + } +- it++; + } ++ if (deleteIds.size()) ++ { ++ updateNoOfSubscribersCount(); ++ persistSubscriptionData(); ++ } ++ ++ BMCWEB_LOG_ERROR << "Map size After Delete : " ++ << subscriptionsMap.size(); + } + + void updateNoOfSubscribersCount() +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README new file mode 100644 index 000000000..c09967456 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README @@ -0,0 +1,34 @@ +Eventservice specific patches: Temporary pulling down +the upstream patches. These will be remove as soon as +thee gets merged upstream. + +Upstream revision information: + - EventService : Add unmerged changes for http retry support (Downstream patch) + file://eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch + + - EventService: https client support + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/31735/40 (Rebased on latest bmcweb) + + - Add Server-Sent-Events support + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41258/9 + + - Add SSE style subscription support to eventservice + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41319/10 + + - Add EventService SSE filter support + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41349/7 (Modified boost::urls::query_params_view to boost::urls::url_view::params_type) + + - EventService Log events for subscription actions (Downstream patch) + file://eventservice/0007-EventService-Log-events-for-subscription-actions.patch + + - Add checks on Event-Subscription input parameters (Downstream patch) + file://eventservice//0008-Add-checks-on-Event-Subscription-input-parameters.patch + + - Restructure Redifsh EventLog Transmit code flow + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/44449/3 + + - Remove Terminated Event Subscriptions (Downstream patch) + file://eventservice/0010-Remove-Terminated-Event-Subscriptions.patch + + - Fix bmcweb crash while deleting terminated subscriptions (Downstream patch) + file://eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0001-Add-asyncResp-support-during-handleUpgrade.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0001-Add-asyncResp-support-during-handleUpgrade.patch new file mode 100644 index 000000000..b3aa11774 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0001-Add-asyncResp-support-during-handleUpgrade.patch @@ -0,0 +1,202 @@ +From f2c3271c8eb405a05a3ec383791e1adc3c4a7f86 Mon Sep 17 00:00:00 2001 +From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +Date: Mon, 18 Oct 2021 22:45:37 +0530 +Subject: [PATCH] Add asyncResp support during handleUpgrade + +The current implementation uses the earlier method of using the response +object and calling response.end() to initiate completion handler. +This commit modifies the implementation to use asyncResp, where the +completion handler gets called asynchronously as the response object +goes out of scope. + +Tested : + - websocket_test.py Passed + - KVM was functional in WebUI. + - POST to /redfish/v1/EventService/Subscriptions/SSE returned an error + message as expected and the connection was kept alive. + - GET on /redfish/v1/EventService/Subscriptions/SSE (SSE subscription) + was successful. The existing connection was successfully closed and + upgraded to SSE connection. + +Change-Id: I2d76b34a49a6432c507d939b21b37c1ced761f8e +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/app.hpp | 6 ++++-- + http/http_connection.hpp | 30 +++++++++++++++++++++++++----- + http/routing.hpp | 37 +++++++++++++++++++++---------------- + 3 files changed, 50 insertions(+), 23 deletions(-) + +diff --git a/http/app.hpp b/http/app.hpp +index 4735197..c46dcf7 100644 +--- a/http/app.hpp ++++ b/http/app.hpp +@@ -45,9 +45,11 @@ class App + } + + template <typename Adaptor> +- void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) ++ void handleUpgrade(const Request& req, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ Adaptor&& adaptor) + { +- router.handleUpgrade(req, res, std::move(adaptor)); ++ router.handleUpgrade(req, asyncResp, std::move(adaptor)); + } + + void handle(Request& req, +diff --git a/http/http_connection.hpp b/http/http_connection.hpp +index 9d53c17..cdd3707 100644 +--- a/http/http_connection.hpp ++++ b/http/http_connection.hpp +@@ -361,6 +361,7 @@ class Connection : + boost::asio::post(self->adaptor.get_executor(), + [self] { self->completeRequest(); }); + }); ++ auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res); + + if ((thisReq.isUpgrade() && + boost::iequals( +@@ -369,13 +370,32 @@ class Connection : + (req->url == "/redfish/v1/EventService/Subscriptions/SSE")) + { + BMCWEB_LOG_DEBUG << "Request: " << this << " is getting upgraded"; +- handler->handleUpgrade(thisReq, res, std::move(adaptor)); +- // delete lambda with self shared_ptr +- // to enable connection destruction +- res.setCompleteRequestHandler(nullptr); ++ res.setCompleteRequestHandler([self(shared_from_this())] { ++ if (self->res.resultInt() != 200) ++ { ++ // When any error occurs during handle upgradation, ++ // the result in response will be set to respective ++ // error. By default the Result will be OK (200), ++ // which implies successful handle upgrade. Response ++ // needs to be sent over this connection only on ++ // failure. ++ boost::asio::post(self->adaptor.get_executor(), ++ [self] { self->completeRequest(); }); ++ } ++ else ++ { ++ // Set Complete request handler to NULL to remove ++ // the shared pointer of connection to enable ++ // connection destruction. As the connection would ++ // get upgraded, we wouldn't need this connection ++ // any longer ++ self->res.setCompleteRequestHandler(nullptr); ++ } ++ }); ++ handler->handleUpgrade(thisReq, asyncResp, std::move(adaptor)); + return; + } +- auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res); ++ + handler->handle(thisReq, asyncResp); + } + +diff --git a/http/routing.hpp b/http/routing.hpp +index 25e4ce8..858f146 100644 +--- a/http/routing.hpp ++++ b/http/routing.hpp +@@ -1202,12 +1202,13 @@ class Router + } + + template <typename Adaptor> +- void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) ++ void handleUpgrade(const Request& req, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ Adaptor&& adaptor) + { + if (static_cast<size_t>(req.method()) >= perMethods.size()) + { +- res.result(boost::beast::http::status::not_found); +- res.end(); ++ asyncResp->res.result(boost::beast::http::status::not_found); + return; + } + +@@ -1220,8 +1221,7 @@ class Router + if (!ruleIndex) + { + BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url; +- res.result(boost::beast::http::status::not_found); +- res.end(); ++ asyncResp->res.result(boost::beast::http::status::not_found); + return; + } + +@@ -1234,23 +1234,24 @@ class Router + { + BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: " + << req.url; +- res.result(boost::beast::http::status::moved_permanently); ++ asyncResp->res.result( ++ boost::beast::http::status::moved_permanently); + + // TODO absolute url building + if (req.getHeaderValue("Host").empty()) + { +- res.addHeader("Location", std::string(req.url) + "/"); ++ asyncResp->res.addHeader("Location", ++ std::string(req.url) + "/"); + } + else + { +- res.addHeader( ++ asyncResp->res.addHeader( + "Location", + req.isSecure + ? "https://" + : "http://" + std::string(req.getHeaderValue("Host")) + + std::string(req.url) + "/"); + } +- res.end(); + return; + } + +@@ -1261,8 +1262,7 @@ class Router + << " with " << req.methodString() << "(" + << static_cast<uint32_t>(req.method()) << ") / " + << rules[ruleIndex]->getMethods(); +- res.result(boost::beast::http::status::not_found); +- res.end(); ++ asyncResp->res.result(boost::beast::http::status::not_found); + return; + } + +@@ -1273,13 +1273,18 @@ class Router + // any uncaught exceptions become 500s + try + { +- rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor)); ++ // Creating temporary response object to call handleUpgrade ++ // We cannot pass the asyncResp as it will be destroyed ++ // The response object is not initialized as handleUpgrade wouldn't ++ // be using this object ++ crow::Response resp; ++ rules[ruleIndex]->handleUpgrade(req, resp, std::move(adaptor)); + } + catch (std::exception& e) + { + BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); +- res.result(boost::beast::http::status::internal_server_error); +- res.end(); ++ asyncResp->res.result( ++ boost::beast::http::status::internal_server_error); + return; + } + catch (...) +@@ -1287,8 +1292,8 @@ class Router + BMCWEB_LOG_ERROR + << "An uncaught exception occurred. The type was unknown " + "so no information was available."; +- res.result(boost::beast::http::status::internal_server_error); +- res.end(); ++ asyncResp->res.result( ++ boost::beast::http::status::internal_server_error); + return; + } + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0002-Move-privileges-to-separate-entity.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0002-Move-privileges-to-separate-entity.patch new file mode 100644 index 000000000..1217147b4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0002-Move-privileges-to-separate-entity.patch @@ -0,0 +1,109 @@ +From 6483f0af926391e8d1f256ba0f23f3640260cfd1 Mon Sep 17 00:00:00 2001 +From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +Date: Mon, 18 Oct 2021 22:52:17 +0530 +Subject: [PATCH] Move privileges to separate entity + +The privilege property of a rule is currently part of RuleParameterTraits +structure. Moving this property (member function) out into a separate +entity PrivilegeParameterTraits. +This move is required to enable inheriting this entity into Weksockets +and SseSockets. + +Tested: + - bmcweb is functional and is responding to Redfish URI's + - User Privilege check for URI's is functional. + +Change-Id: I288ab12258c15ae5a626f4409fc3b4a9cc574ea3 +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/routing.hpp | 53 +++++++++++++++++++++++++++--------------------- + 1 file changed, 30 insertions(+), 23 deletions(-) + +diff --git a/http/routing.hpp b/http/routing.hpp +index 858f146..acc99dc 100644 +--- a/http/routing.hpp ++++ b/http/routing.hpp +@@ -102,6 +102,8 @@ class BaseRule + friend class Router; + template <typename T> + friend struct RuleParameterTraits; ++ template <typename T> ++ friend struct PrivilegeParameterTraits; + }; + + namespace detail +@@ -316,6 +318,33 @@ struct Wrapped + } // namespace routing_handler_call_helper + } // namespace detail + ++template <typename T> ++struct PrivilegeParameterTraits ++{ ++ using self_t = T; ++ self_t& privileges( ++ const std::initializer_list<std::initializer_list<const char*>>& p) ++ { ++ self_t* self = static_cast<self_t*>(this); ++ for (const std::initializer_list<const char*>& privilege : p) ++ { ++ self->privilegesSet.emplace_back(privilege); ++ } ++ return *self; ++ } ++ ++ template <size_t N, typename... MethodArgs> ++ self_t& privileges(const std::array<redfish::Privileges, N>& p) ++ { ++ self_t* self = static_cast<self_t*>(this); ++ for (const redfish::Privileges& privilege : p) ++ { ++ self->privilegesSet.emplace_back(privilege); ++ } ++ return *self; ++ } ++}; ++ + class WebSocketRule : public BaseRule + { + using self_t = WebSocketRule; +@@ -462,7 +491,7 @@ class SseSocketRule : public BaseRule + }; + + template <typename T> +-struct RuleParameterTraits ++struct RuleParameterTraits : public PrivilegeParameterTraits<T> + { + using self_t = T; + WebSocketRule& websocket() +@@ -503,28 +532,6 @@ struct RuleParameterTraits + self->methodsBitfield |= 1U << static_cast<size_t>(method); + return *self; + } +- +- self_t& privileges( +- const std::initializer_list<std::initializer_list<const char*>>& p) +- { +- self_t* self = static_cast<self_t*>(this); +- for (const std::initializer_list<const char*>& privilege : p) +- { +- self->privilegesSet.emplace_back(privilege); +- } +- return *self; +- } +- +- template <size_t N, typename... MethodArgs> +- self_t& privileges(const std::array<redfish::Privileges, N>& p) +- { +- self_t* self = static_cast<self_t*>(this); +- for (const redfish::Privileges& privilege : p) +- { +- self->privilegesSet.emplace_back(privilege); +- } +- return *self; +- } + }; + + class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule> +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0003-Add-Support-for-privilege-check-in-handleUpgrade.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0003-Add-Support-for-privilege-check-in-handleUpgrade.patch new file mode 100644 index 000000000..1ba584616 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0003-Add-Support-for-privilege-check-in-handleUpgrade.patch @@ -0,0 +1,218 @@ +From aabe4718b8e6c1f7b91af29cbaf85d5fa1fa0a99 Mon Sep 17 00:00:00 2001 +From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +Date: Mon, 18 Oct 2021 22:55:38 +0530 +Subject: [PATCH] Add Support for privilege check in handleUpgrade + +This commit enables privilege check for user(s) in case of upgraded +connections. +Currently users with no privileges will also be able to access +Websockets connections (Ex: KVM). + +Tested: + - websocket_test.py Passed + - Admin and Operator users were able to access KVM on WebUI + - Readonly User was unable to access KVM on WebUI + +Change-Id: Id9d33aeca24d8fafb2e9dcc28c46a48930740cd6 +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/app.hpp | 2 +- + http/routing.hpp | 162 +++++++++++++++++++++++++++++++++++++++-------- + 2 files changed, 136 insertions(+), 28 deletions(-) + +diff --git a/http/app.hpp b/http/app.hpp +index c46dcf7..dd51eee 100644 +--- a/http/app.hpp ++++ b/http/app.hpp +@@ -45,7 +45,7 @@ class App + } + + template <typename Adaptor> +- void handleUpgrade(const Request& req, ++ void handleUpgrade(Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + Adaptor&& adaptor) + { +diff --git a/http/routing.hpp b/http/routing.hpp +index acc99dc..e2a8fbb 100644 +--- a/http/routing.hpp ++++ b/http/routing.hpp +@@ -1209,7 +1209,7 @@ class Router + } + + template <typename Adaptor> +- void handleUpgrade(const Request& req, ++ void handleUpgrade(Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + Adaptor&& adaptor) + { +@@ -1277,32 +1277,140 @@ class Router + << "' " << static_cast<uint32_t>(req.method()) << " / " + << rules[ruleIndex]->getMethods(); + +- // any uncaught exceptions become 500s +- try +- { +- // Creating temporary response object to call handleUpgrade +- // We cannot pass the asyncResp as it will be destroyed +- // The response object is not initialized as handleUpgrade wouldn't +- // be using this object +- crow::Response resp; +- rules[ruleIndex]->handleUpgrade(req, resp, std::move(adaptor)); +- } +- catch (std::exception& e) +- { +- BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); +- asyncResp->res.result( +- boost::beast::http::status::internal_server_error); +- return; +- } +- catch (...) +- { +- BMCWEB_LOG_ERROR +- << "An uncaught exception occurred. The type was unknown " +- "so no information was available."; +- asyncResp->res.result( +- boost::beast::http::status::internal_server_error); +- return; +- } ++ crow::connections::systemBus->async_method_call( ++ [&req, asyncResp, &rules, ruleIndex, &adaptor]( ++ const boost::system::error_code ec, ++ std::map<std::string, std::variant<bool, std::string, ++ std::vector<std::string>>> ++ userInfo) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "GetUserInfo failed..."; ++ asyncResp->res.result( ++ boost::beast::http::status::internal_server_error); ++ return; ++ } ++ ++ const std::string* userRolePtr = nullptr; ++ auto userInfoIter = userInfo.find("UserPrivilege"); ++ if (userInfoIter != userInfo.end()) ++ { ++ userRolePtr = ++ std::get_if<std::string>(&userInfoIter->second); ++ } ++ ++ std::string userRole{}; ++ if (userRolePtr != nullptr) ++ { ++ userRole = *userRolePtr; ++ BMCWEB_LOG_DEBUG << "userName = " << req.session->username ++ << " userRole = " << *userRolePtr; ++ } ++ ++ bool* remoteUserPtr = nullptr; ++ auto remoteUserIter = userInfo.find("RemoteUser"); ++ if (remoteUserIter != userInfo.end()) ++ { ++ remoteUserPtr = std::get_if<bool>(&remoteUserIter->second); ++ } ++ if (remoteUserPtr == nullptr) ++ { ++ BMCWEB_LOG_ERROR ++ << "RemoteUser property missing or wrong type"; ++ asyncResp->res.result( ++ boost::beast::http::status::internal_server_error); ++ return; ++ } ++ bool remoteUser = *remoteUserPtr; ++ ++ bool passwordExpired = false; // default for remote user ++ if (!remoteUser) ++ { ++ bool* passwordExpiredPtr = nullptr; ++ auto passwordExpiredIter = ++ userInfo.find("UserPasswordExpired"); ++ if (passwordExpiredIter != userInfo.end()) ++ { ++ passwordExpiredPtr = ++ std::get_if<bool>(&passwordExpiredIter->second); ++ } ++ if (passwordExpiredPtr != nullptr) ++ { ++ passwordExpired = *passwordExpiredPtr; ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR ++ << "UserPasswordExpired property is expected for" ++ " local user but is missing or wrong type"; ++ asyncResp->res.result( ++ boost::beast::http::status::internal_server_error); ++ return; ++ } ++ } ++ ++ // Get the userprivileges from the role ++ redfish::Privileges userPrivileges = ++ redfish::getUserPrivileges(userRole); ++ ++ // Set isConfigureSelfOnly based on D-Bus results. This ++ // ignores the results from both pamAuthenticateUser and the ++ // value from any previous use of this session. ++ req.session->isConfigureSelfOnly = passwordExpired; ++ ++ // Modifyprivileges if isConfigureSelfOnly. ++ if (req.session->isConfigureSelfOnly) ++ { ++ // Remove allprivileges except ConfigureSelf ++ userPrivileges = userPrivileges.intersection( ++ redfish::Privileges{"ConfigureSelf"}); ++ BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf"; ++ } ++ ++ if (!rules[ruleIndex]->checkPrivileges(userPrivileges)) ++ { ++ asyncResp->res.result( ++ boost::beast::http::status::forbidden); ++ if (req.session->isConfigureSelfOnly) ++ { ++ redfish::messages::passwordChangeRequired( ++ asyncResp->res, ++ "/redfish/v1/AccountService/Accounts/" + ++ req.session->username); ++ } ++ return; ++ } ++ ++ req.userRole = userRole; ++ ++ // any uncaught exceptions become 500s ++ try ++ { ++ crow::Response resp; ++ rules[ruleIndex]->handleUpgrade(req, resp, ++ std::move(adaptor)); ++ } ++ catch (std::exception& e) ++ { ++ BMCWEB_LOG_ERROR << "An uncaught exception occurred: " ++ << e.what(); ++ asyncResp->res.result( ++ boost::beast::http::status::internal_server_error); ++ return; ++ } ++ catch (...) ++ { ++ BMCWEB_LOG_ERROR ++ << "An uncaught exception occurred. The type was " ++ "unknown so no information was available."; ++ asyncResp->res.result( ++ boost::beast::http::status::internal_server_error); ++ return; ++ } ++ }, ++ "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", ++ "xyz.openbmc_project.User.Manager", "GetUserInfo", ++ req.session->username); + } + + void handle(Request& req, +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0004-Add-Privileges-to-Websockets.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0004-Add-Privileges-to-Websockets.patch new file mode 100644 index 000000000..64e235ce3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0004-Add-Privileges-to-Websockets.patch @@ -0,0 +1,140 @@ +From 9b27d3e7c1670d53cfb1c0a88cc75155ebfba71a Mon Sep 17 00:00:00 2001 +From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +Date: Mon, 18 Oct 2021 22:58:29 +0530 +Subject: [PATCH] Add Privileges to Websockets + +This commit adds Privileges to Websockets. +In the current implementation, once a rule is upgraded (i.e. from +BaseRule to WebSocket), there is no provosion to add priviliges. +In this commit, WebSocket inherits PrivilegeParameterTraits to enable +privileges. + +Also, in the earlier implementation, .privilege() was called after +BMCWEB_ROUTE(). This results in adding those privileges to the Base rule +that is created. By moving the privileges() below websocket(), the +privileges are applied to the websocket. + +Tested: + - websocket_test.py Passed + - Admin and Operator users were able to access KVM on WebUI + - Readonly User was unable to access KVM on WebUI + +Change-Id: Iff2051dbb7d363c902fd463fa446f280adc6d648 +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/routing.hpp | 4 +++- + include/dbus_monitor.hpp | 3 ++- + include/kvm_websocket.hpp | 4 +++- + include/obmc_console.hpp | 4 +++- + include/vm_websocket.hpp | 4 +++- + 5 files changed, 14 insertions(+), 5 deletions(-) + +diff --git a/http/routing.hpp b/http/routing.hpp +index e2a8fbb..6ea3185 100644 +--- a/http/routing.hpp ++++ b/http/routing.hpp +@@ -345,7 +345,9 @@ struct PrivilegeParameterTraits + } + }; + +-class WebSocketRule : public BaseRule ++class WebSocketRule : ++ public BaseRule, ++ public PrivilegeParameterTraits<WebSocketRule> + { + using self_t = WebSocketRule; + +diff --git a/include/dbus_monitor.hpp b/include/dbus_monitor.hpp +index a6c86c6..163f884 100644 +--- a/include/dbus_monitor.hpp ++++ b/include/dbus_monitor.hpp +@@ -5,6 +5,7 @@ + #include <boost/container/flat_set.hpp> + #include <dbus_singleton.hpp> + #include <openbmc_dbus_rest.hpp> ++#include <registries/privilege_registry.hpp> + #include <sdbusplus/bus/match.hpp> + #include <sdbusplus/message/types.hpp> + #include <websocket.hpp> +@@ -105,8 +106,8 @@ inline int onPropertyUpdate(sd_bus_message* m, void* userdata, + inline void requestRoutes(App& app) + { + BMCWEB_ROUTE(app, "/subscribe") +- .privileges({{"Login"}}) + .websocket() ++ .privileges(redfish::privileges::privilegeSetLogin) + .onopen([&](crow::websocket::Connection& conn, + const std::shared_ptr<bmcweb::AsyncResp>&) { + BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; +diff --git a/include/kvm_websocket.hpp b/include/kvm_websocket.hpp +index a9dc8ea..3f124a2 100644 +--- a/include/kvm_websocket.hpp ++++ b/include/kvm_websocket.hpp +@@ -4,6 +4,7 @@ + #include <app.hpp> + #include <async_resp.hpp> + #include <boost/container/flat_map.hpp> ++#include <registries/privilege_registry.hpp> + #include <websocket.hpp> + + namespace crow +@@ -159,8 +160,9 @@ inline void requestRoutes(App& app) + sessions.reserve(maxSessions); + + BMCWEB_ROUTE(app, "/kvm/0") +- .privileges({{"ConfigureComponents", "ConfigureManager"}}) + .websocket() ++ .privileges(redfish::privileges:: ++ privilegeSetConfigureManagerOrConfigureComponents) + .onopen([](crow::websocket::Connection& conn, + const std::shared_ptr<bmcweb::AsyncResp>&) { + BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; +diff --git a/include/obmc_console.hpp b/include/obmc_console.hpp +index ff0a51f..22a49a8 100644 +--- a/include/obmc_console.hpp ++++ b/include/obmc_console.hpp +@@ -6,6 +6,7 @@ + #include <boost/asio/local/stream_protocol.hpp> + #include <boost/container/flat_map.hpp> + #include <boost/container/flat_set.hpp> ++#include <registries/privilege_registry.hpp> + #include <websocket.hpp> + + namespace crow +@@ -136,8 +137,9 @@ inline void connectHandler(const boost::system::error_code& ec) + inline void requestRoutes(App& app) + { + BMCWEB_ROUTE(app, "/console0") +- .privileges({{"ConfigureComponents", "ConfigureManager"}}) + .websocket() ++ .privileges(redfish::privileges:: ++ privilegeSetConfigureManagerOrConfigureComponents) + .onopen([](crow::websocket::Connection& conn, + const std::shared_ptr<bmcweb::AsyncResp>&) { + BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; +diff --git a/include/vm_websocket.hpp b/include/vm_websocket.hpp +index 02f958a..ebbe68f 100644 +--- a/include/vm_websocket.hpp ++++ b/include/vm_websocket.hpp +@@ -3,6 +3,7 @@ + #include <app.hpp> + #include <boost/beast/core/flat_static_buffer.hpp> + #include <boost/process.hpp> ++#include <registries/privilege_registry.hpp> + #include <websocket.hpp> + + #include <csignal> +@@ -156,8 +157,9 @@ static std::shared_ptr<Handler> handler; + inline void requestRoutes(App& app) + { + BMCWEB_ROUTE(app, "/vm/0/0") +- .privileges({{"ConfigureComponents", "ConfigureManager"}}) + .websocket() ++ .privileges(redfish::privileges:: ++ privilegeSetConfigureManagerOrConfigureComponents) + .onopen([](crow::websocket::Connection& conn, + const std::shared_ptr<bmcweb::AsyncResp>&) { + BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0005-Add-Privileges-to-SseSockets.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0005-Add-Privileges-to-SseSockets.patch new file mode 100644 index 000000000..06ffb3a46 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0005-Add-Privileges-to-SseSockets.patch @@ -0,0 +1,63 @@ +From 0ceb343809ff498cbfa389c54a158d255a2cca88 Mon Sep 17 00:00:00 2001 +From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +Date: Mon, 18 Oct 2021 23:02:00 +0530 +Subject: [PATCH] Add Privileges to SseSockets + +This commit adds Privileges to Ssesockets. +In the current implementation, once a rule is upgraded (i.e. from +BaseRule to SseSocket), there is no provision to add priviliges. +In this commit, SseSocket inherits PrivilegeParameterTraits to +enable privileges. + +Also, in the earlier implementation, .privilege() was called after +BMCWEB_ROUTE(). This results in adding those privileges to the Base +rule that is created. By moving the privileges() below websocket(), +the privileges are applied to the Ssesocket. + +Tested: + - SSE Subscription was successful with Admin and Operator Users + - SSE Subscription was rejected while using Readonly User + - websocket_test.py Passed + - Admin and Operator users were able to access KVM on WebUI + - Readonly User was unable to access KVM on WebUI + +Change-Id: I41739401893b1c2bf718e11ec7676d69f954c98f +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + http/routing.hpp | 4 +++- + include/eventservice_sse.hpp | 3 ++- + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/http/routing.hpp b/http/routing.hpp +index 6ea3185..13174b2 100644 +--- a/http/routing.hpp ++++ b/http/routing.hpp +@@ -430,7 +430,9 @@ class WebSocketRule : + std::function<void(crow::websocket::Connection&)> errorHandler; + }; + +-class SseSocketRule : public BaseRule ++class SseSocketRule : ++ public BaseRule, ++ public PrivilegeParameterTraits<SseSocketRule> + { + using self_t = SseSocketRule; + +diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp +index 2f22f98..f880344 100644 +--- a/include/eventservice_sse.hpp ++++ b/include/eventservice_sse.hpp +@@ -192,8 +192,9 @@ static void deleteSubscription(std::shared_ptr<crow::SseConnection>& conn) + inline void requestRoutes(App& app) + { + BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/SSE") +- .privileges({{"ConfigureComponents", "ConfigureManager"}}) + .serverSentEvent() ++ .privileges(redfish::privileges:: ++ privilegeSetConfigureManagerOrConfigureComponents) + .onopen([](std::shared_ptr<crow::SseConnection>& conn, + const crow::Request& req, crow::Response& res) { + BMCWEB_LOG_DEBUG << "Connection " << conn << " opened."; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch new file mode 100644 index 000000000..f5226fe6e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch @@ -0,0 +1,619 @@ +From 32e557279450226ed9c06312649d90b802f3d4c5 Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Tue, 13 Apr 2021 13:00:18 +0000 +Subject: [PATCH] Add support for MetricDefinition scheme + +Added MetricDefinition node to Redfish code. Now user is able to list +all available metrics in OpenBMC that are supported by Telemetry +service. Metrics are grouped by reading type. + +MetricDefinitions contains all physical sensors supported by redfish, +algorithm iterates through all chassis and collects results for each +node available in that chassis (Power, Thermal, Sensors). + +When BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM will be enabled by +default (meson option redfish-new-powersubsystem-thermalsubsystem) it +will be possible to optimize this algorithm to only get sensors from +Sensors node. Currently Sensors node doesn't contain all available +sensors. + +Tested: + - MetricDefinitions response is filled with existing sensors, it works + with and without Telemetry service + - Validated a presence of MetricDefinition members and its attributes + - Successfully passed RedfishServiceValidator.py using witherspoon + image on QEMU + - Tested using following GET,POST requests + +GET /redfish/v1/TelemetryService/MetricDefinitions +{ + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions", + "@odata.type": "#MetricDefinitionCollection.MetricDefinitionCollection", + "Members": [ + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Fan_Pwm" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Fan_Tach" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/HostCpuUtilization" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/HostMemoryBandwidthUtilization" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/HostPciBandwidthUtilization" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Inlet_BRD_Temp" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Left_Rear_Board_Temp" + } + ], + "Members@odata.count": 7, + "Name": "Metric Definition Collection" +} + +GET /redfish/v1/TelemetryService/MetricDefinitions/Fan_Tach +{ + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Fan_Tach", + "@odata.type": "#MetricDefinition.v1_0_3.MetricDefinition", + "Id": "Fan_Tach", + "IsLinear": true, + "MaxReadingRange": 25000.0, + "MetricDataType": "Decimal", + "MetricProperties": [ + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/0/Reading", + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/1/Reading", + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/2/Reading", + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/3/Reading", + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/4/Reading", + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/5/Reading", + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/6/Reading", + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/7/Reading", + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/8/Reading", + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/9/Reading" + ], + "MetricType": "Gauge", + "MinReadingRange": 0.0, + "Name": "Fan_Tach", + "Units": "RPM" +} + +POST redfish/v1/TelemetryService/MetricReportDefinitions, body: +{ + "Id": "TestReport", + "Metrics": [ + { + "MetricId": "TestMetric", + "MetricProperties": [ + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/3/Reading", + ] + } + ], + "MetricReportDefinitionType": "OnRequest", + "ReportActions": [ + "RedfishEvent", + "LogToMetricReportsCollection" + ] +} +{ + "@Message.ExtendedInfo": [ + { + "@odata.type": "#Message.v1_1_1.Message", + "Message": "The resource has been created successfully", + "MessageArgs": [], + "MessageId": "Base.1.8.1.Created", + "MessageSeverity": "OK", + "Resolution": "None" + } + ] +} + +Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com> +Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Change-Id: I3086e1302e1ba2e5442d1367939fd5507a0cbc00 +--- + redfish-core/include/redfish.hpp | 3 + + .../include/utils/get_chassis_names.hpp | 58 +++ + .../include/utils/telemetry_utils.hpp | 2 + + redfish-core/lib/metric_definition.hpp | 368 ++++++++++++++++++ + redfish-core/lib/telemetry_service.hpp | 3 +- + 5 files changed, 433 insertions(+), 1 deletion(-) + create mode 100644 redfish-core/include/utils/get_chassis_names.hpp + create mode 100644 redfish-core/lib/metric_definition.hpp + +diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp +index 0a97150..67c5af2 100644 +--- a/redfish-core/include/redfish.hpp ++++ b/redfish-core/include/redfish.hpp +@@ -26,6 +26,7 @@ + #include "../lib/managers.hpp" + #include "../lib/memory.hpp" + #include "../lib/message_registries.hpp" ++#include "../lib/metric_definition.hpp" + #include "../lib/metric_report.hpp" + #include "../lib/metric_report_definition.hpp" + #include "../lib/network_protocol.hpp" +@@ -200,6 +201,8 @@ class RedfishService + requestRoutesMetricReportDefinition(app); + requestRoutesMetricReportCollection(app); + requestRoutesMetricReport(app); ++ requestRoutesMetricDefinitionCollection(app); ++ requestRoutesMetricDefinition(app); + } + }; + +diff --git a/redfish-core/include/utils/get_chassis_names.hpp b/redfish-core/include/utils/get_chassis_names.hpp +new file mode 100644 +index 0000000..0276b6f +--- /dev/null ++++ b/redfish-core/include/utils/get_chassis_names.hpp +@@ -0,0 +1,58 @@ ++#pragma once ++ ++#include <include/dbus_singleton.hpp> ++ ++#include <array> ++#include <string> ++#include <vector> ++ ++namespace redfish ++{ ++ ++namespace utils ++{ ++ ++template <typename F> ++inline void getChassisNames(F&& cb) ++{ ++ const std::array<const char*, 2> interfaces = { ++ "xyz.openbmc_project.Inventory.Item.Board", ++ "xyz.openbmc_project.Inventory.Item.Chassis"}; ++ ++ crow::connections::systemBus->async_method_call( ++ [callback = std::move(cb)](const boost::system::error_code ec, ++ const std::vector<std::string>& chassis) { ++ std::vector<std::string> chassisNames; ++ ++ if (ec) ++ { ++ callback(ec, chassisNames); ++ return; ++ } ++ ++ chassisNames.reserve(chassis.size()); ++ for (const std::string& path : chassis) ++ { ++ sdbusplus::message::object_path dbusPath = path; ++ std::string name = dbusPath.filename(); ++ if (name.empty()) ++ { ++ callback(boost::system::errc::make_error_code( ++ boost::system::errc::invalid_argument), ++ chassisNames); ++ return; ++ } ++ chassisNames.emplace_back(std::move(name)); ++ } ++ ++ callback(ec, chassisNames); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", ++ "/xyz/openbmc_project/inventory", 0, interfaces); ++} ++ ++} // namespace utils ++ ++} // namespace redfish +diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp +index 5872350..1b4f75d 100644 +--- a/redfish-core/include/utils/telemetry_utils.hpp ++++ b/redfish-core/include/utils/telemetry_utils.hpp +@@ -10,6 +10,8 @@ namespace telemetry + + constexpr const char* service = "xyz.openbmc_project.Telemetry"; + constexpr const char* reportInterface = "xyz.openbmc_project.Telemetry.Report"; ++constexpr const char* metricDefinitionUri = ++ "/redfish/v1/TelemetryService/MetricDefinitions/"; + constexpr const char* metricReportDefinitionUri = + "/redfish/v1/TelemetryService/MetricReportDefinitions/"; + constexpr const char* metricReportUri = +diff --git a/redfish-core/lib/metric_definition.hpp b/redfish-core/lib/metric_definition.hpp +new file mode 100644 +index 0000000..347c297 +--- /dev/null ++++ b/redfish-core/lib/metric_definition.hpp +@@ -0,0 +1,368 @@ ++#pragma once ++ ++#include "async_resp.hpp" ++#include "sensors.hpp" ++#include "utils/get_chassis_names.hpp" ++#include "utils/telemetry_utils.hpp" ++ ++#include <registries/privilege_registry.hpp> ++ ++namespace redfish ++{ ++ ++namespace telemetry ++{ ++ ++struct ValueVisitor ++{ ++ ValueVisitor(boost::system::error_code& ec) : ec(ec) ++ {} ++ ++ template <class T> ++ double operator()(T value) const ++ { ++ return static_cast<double>(value); ++ } ++ ++ double operator()(std::monostate) const ++ { ++ ec = boost::system::errc::make_error_code( ++ boost::system::errc::invalid_argument); ++ return double{}; ++ } ++ ++ boost::system::error_code& ec; ++}; ++ ++inline void getReadingRange( ++ const std::string& service, const std::string& path, ++ const std::string& property, ++ std::function<void(boost::system::error_code, double)> callback) ++{ ++ crow::connections::systemBus->async_method_call( ++ [callback = std::move(callback)]( ++ boost::system::error_code ec, ++ const std::variant<std::monostate, double, uint64_t, int64_t, ++ uint32_t, int32_t, uint16_t, int16_t>& ++ valueVariant) { ++ if (ec) ++ { ++ callback(ec, double{}); ++ return; ++ } ++ ++ const double value = std::visit(ValueVisitor(ec), valueVariant); ++ ++ callback(ec, value); ++ }, ++ service, path, "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.Sensor.Value", property); ++} ++ ++inline void ++ fillMinMaxReadingRange(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ const std::string& serviceName, ++ const std::string& sensorPath) ++{ ++ asyncResp->res.jsonValue["MetricType"] = "Numeric"; ++ ++ telemetry::getReadingRange( ++ serviceName, sensorPath, "MinValue", ++ [asyncResp](boost::system::error_code ec, double readingRange) { ++ if (ec) ++ { ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ if (std::isfinite(readingRange)) ++ { ++ asyncResp->res.jsonValue["MetricType"] = "Gauge"; ++ ++ asyncResp->res.jsonValue["MinReadingRange"] = readingRange; ++ } ++ }); ++ ++ telemetry::getReadingRange( ++ serviceName, sensorPath, "MaxValue", ++ [asyncResp](boost::system::error_code ec, double readingRange) { ++ if (ec) ++ { ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ if (std::isfinite(readingRange)) ++ { ++ asyncResp->res.jsonValue["MetricType"] = "Gauge"; ++ ++ asyncResp->res.jsonValue["MaxReadingRange"] = readingRange; ++ } ++ }); ++} ++ ++inline void getSensorService( ++ const std::string& sensorPath, ++ std::function<void(boost::system::error_code, const std::string&)> callback) ++{ ++ using ResultType = std::pair< ++ std::string, ++ std::vector<std::pair<std::string, std::vector<std::string>>>>; ++ ++ crow::connections::systemBus->async_method_call( ++ [sensorPath, callback = std::move(callback)]( ++ boost::system::error_code ec, ++ const std::vector<ResultType>& result) { ++ if (ec) ++ { ++ callback(ec, std::string{}); ++ return; ++ } ++ ++ for (const auto& [path, serviceToInterfaces] : result) ++ { ++ if (path == sensorPath) ++ { ++ for (const auto& [service, interfaces] : ++ serviceToInterfaces) ++ { ++ callback(boost::system::errc::make_error_code( ++ boost::system::errc::success), ++ service); ++ return; ++ } ++ } ++ } ++ ++ callback(boost::system::errc::make_error_code( ++ boost::system::errc::no_such_file_or_directory), ++ std::string{}); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTree", ++ "/xyz/openbmc_project/sensors", 2, ++ std::array{"xyz.openbmc_project.Sensor.Value"}); ++} ++ ++constexpr auto metricDefinitionMapping = std::array{ ++ std::pair{"fan_pwm", "Fan_Pwm"}, std::pair{"fan_tach", "Fan_Tach"}}; ++ ++std::string mapSensorToMetricDefinition(const std::string& sensorPath) ++{ ++ sdbusplus::message::object_path sensorObjectPath{sensorPath}; ++ ++ const auto it = std::find_if( ++ metricDefinitionMapping.begin(), metricDefinitionMapping.end(), ++ [&sensorObjectPath](const auto& item) { ++ return item.first == sensorObjectPath.parent_path().filename(); ++ }); ++ ++ const char* metricDefinitionPath = ++ "/redfish/v1/TelemetryService/MetricDefinitions/"; ++ ++ if (it != metricDefinitionMapping.end()) ++ { ++ return std::string{metricDefinitionPath} + it->second; ++ } ++ ++ return metricDefinitionPath + sensorObjectPath.filename(); ++} ++ ++template <class Callback> ++inline void mapRedfishUriToDbusPath(Callback&& callback) ++{ ++ utils::getChassisNames([callback = std::move(callback)]( ++ boost::system::error_code ec, ++ const std::vector<std::string>& chassisNames) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "getChassisNames error: " << ec.value(); ++ callback(ec, {}); ++ return; ++ } ++ ++ auto counter = std::make_shared<std::pair< ++ boost::container::flat_map<std::string, std::string>, size_t>>(); ++ ++ auto handleRetrieveUriToDbusMap = ++ [counter, callback = std::move(callback)]( ++ const boost::beast::http::status status, ++ const boost::container::flat_map<std::string, std::string>& ++ uriToDbus) { ++ if (status != boost::beast::http::status::ok) ++ { ++ BMCWEB_LOG_ERROR << "Failed to retrieve URI to dbus " ++ "sensors map with err " ++ << static_cast<unsigned>(status); ++ counter->second = 0u; ++ callback(boost::system::errc::make_error_code( ++ boost::system::errc::io_error), ++ {}); ++ return; ++ } ++ ++ for (const auto& [key, value] : uriToDbus) ++ { ++ counter->first[key] = value; ++ } ++ ++ if (--counter->second == 0u) ++ { ++ callback(boost::system::errc::make_error_code( ++ boost::system::errc::success), ++ counter->first); ++ } ++ }; ++ ++ for (const std::string& chassisName : chassisNames) ++ { ++ for (const auto& [sensorNode, dbusPaths] : sensors::dbus::paths) ++ { ++ ++counter->second; ++ retrieveUriToDbusMap(chassisName, sensorNode.data(), ++ handleRetrieveUriToDbusMap); ++ } ++ } ++ }); ++} ++ ++} // namespace telemetry ++ ++inline void requestRoutesMetricDefinitionCollection(App& app) ++{ ++ BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricDefinitions/") ++ .privileges(privileges::getTelemetryService) ++ .methods(boost::beast::http::verb::get)( ++ [](const crow::Request&, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ telemetry::mapRedfishUriToDbusPath( ++ [asyncResp](boost::system::error_code ec, ++ const boost::container::flat_map< ++ std::string, std::string>& uriToDbus) { ++ if (ec) ++ { ++ messages::internalError(asyncResp->res); ++ BMCWEB_LOG_ERROR ++ << "mapRedfishUriToDbusPath error: " ++ << ec.value(); ++ return; ++ } ++ ++ std::set<std::string> members; ++ ++ for (const auto& [uri, dbusPath] : uriToDbus) ++ { ++ members.insert( ++ telemetry::mapSensorToMetricDefinition( ++ dbusPath)); ++ } ++ ++ for (const std::string& odataId : members) ++ { ++ asyncResp->res.jsonValue["Members"].push_back( ++ {{"@odata.id", odataId}}); ++ } ++ ++ asyncResp->res.jsonValue["Members@odata.count"] = ++ asyncResp->res.jsonValue["Members"].size(); ++ }); ++ ++ asyncResp->res.jsonValue["@odata.type"] = ++ "#MetricDefinitionCollection." ++ "MetricDefinitionCollection"; ++ asyncResp->res.jsonValue["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricDefinitions"; ++ asyncResp->res.jsonValue["Name"] = ++ "Metric Definition Collection"; ++ asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); ++ asyncResp->res.jsonValue["Members@odata.count"] = 0; ++ }); ++} ++ ++inline void requestRoutesMetricDefinition(App& app) ++{ ++ BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricDefinitions/<str>/") ++ .privileges(privileges::getTelemetryService) ++ .methods( ++ boost::beast::http::verb::get)([](const crow::Request&, ++ const std::shared_ptr< ++ bmcweb::AsyncResp>& asyncResp, ++ const std::string& name) { ++ telemetry::mapRedfishUriToDbusPath( ++ [asyncResp, name]( ++ boost::system::error_code ec, ++ const boost::container::flat_map<std::string, std::string>& ++ uriToDbus) { ++ if (ec) ++ { ++ messages::internalError(asyncResp->res); ++ BMCWEB_LOG_ERROR << "mapRedfishUriToDbusPath error: " ++ << ec.value(); ++ return; ++ } ++ ++ std::string odataId = telemetry::metricDefinitionUri + name; ++ boost::container::flat_map<std::string, std::string> ++ matchingUris; ++ ++ for (const auto& [uri, dbusPath] : uriToDbus) ++ { ++ if (telemetry::mapSensorToMetricDefinition(dbusPath) == ++ odataId) ++ { ++ matchingUris.emplace(uri, dbusPath); ++ } ++ } ++ ++ if (matchingUris.empty()) ++ { ++ messages::resourceNotFound(asyncResp->res, ++ "MetricDefinition", name); ++ return; ++ } ++ ++ std::string sensorPath = matchingUris.begin()->second; ++ ++ telemetry::getSensorService( ++ sensorPath, ++ [asyncResp, name, odataId = std::move(odataId), ++ sensorPath, matchingUris = std::move(matchingUris)]( ++ boost::system::error_code ec, ++ const std::string& serviceName) { ++ if (ec) ++ { ++ messages::internalError(asyncResp->res); ++ BMCWEB_LOG_ERROR << "getServiceSensorFailed: " ++ << ec.value(); ++ return; ++ } ++ ++ asyncResp->res.jsonValue["Id"] = name; ++ asyncResp->res.jsonValue["Name"] = name; ++ asyncResp->res.jsonValue["@odata.id"] = odataId; ++ asyncResp->res.jsonValue["@odata.type"] = ++ "#MetricDefinition.v1_0_3.MetricDefinition"; ++ asyncResp->res.jsonValue["MetricDataType"] = ++ "Decimal"; ++ asyncResp->res.jsonValue["IsLinear"] = true; ++ asyncResp->res.jsonValue["Units"] = ++ sensors::toReadingUnits( ++ sdbusplus::message::object_path{sensorPath} ++ .parent_path() ++ .filename()); ++ ++ for (const auto& [uri, dbusPath] : matchingUris) ++ { ++ asyncResp->res.jsonValue["MetricProperties"] ++ .push_back(uri); ++ } ++ ++ telemetry::fillMinMaxReadingRange( ++ asyncResp, serviceName, sensorPath); ++ }); ++ }); ++ }); ++} ++ ++} // namespace redfish +diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp +index 8ecc591..027b51b 100644 +--- a/redfish-core/lib/telemetry_service.hpp ++++ b/redfish-core/lib/telemetry_service.hpp +@@ -18,11 +18,12 @@ inline void handleTelemetryServiceGet( + asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/TelemetryService"; + asyncResp->res.jsonValue["Id"] = "TelemetryService"; + asyncResp->res.jsonValue["Name"] = "Telemetry Service"; +- + asyncResp->res.jsonValue["MetricReportDefinitions"]["@odata.id"] = + "/redfish/v1/TelemetryService/MetricReportDefinitions"; + asyncResp->res.jsonValue["MetricReports"]["@odata.id"] = + "/redfish/v1/TelemetryService/MetricReports"; ++ asyncResp->res.jsonValue["MetricDefinitions"]["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricDefinitions"; + + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec, +-- +2.25.1 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Sync-Telmetry-service-with-EventService.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Sync-Telmetry-service-with-EventService.patch new file mode 100644 index 000000000..3088a7f9d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Sync-Telmetry-service-with-EventService.patch @@ -0,0 +1,295 @@ +From 541353a4e4b06de42b6a9a400629f5a5fba04e86 Mon Sep 17 00:00:00 2001 +From: "Wludzik, Jozef" <jozef.wludzik@intel.com> +Date: Tue, 15 Dec 2020 12:30:31 +0100 +Subject: [PATCH] Sync Telmetry service with EventService + +Synced the latest changes in Telemetry service with Event Service +code. Now assembling MetricReport is covered in single place in +code. Updated method of fetching Readings from Telemetry by +Event Service. Using ReportUpdate signal is no longer +supported. Now Event Service monitors for PropertiesChanged signal +from /xyz/openbmc_project/Telemetry/Reports path. + +Tested: + - Verified that EventListener received MetricReport response from + Event Service in insecure http push style eventing mode + +Change-Id: I2fc1841a6c9259a8bff30b34bddc0d4aabd41912 +Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com> +Signed-off-by: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com> +--- + .../include/event_service_manager.hpp | 156 ++++++------------ + redfish-core/lib/metric_report.hpp | 28 ++-- + 2 files changed, 69 insertions(+), 115 deletions(-) + +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 3f398d7..cf9f658 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -14,6 +14,7 @@ + // limitations under the License. + */ + #pragma once ++#include "metric_report.hpp" + #include "registries.hpp" + #include "registries/base_message_registry.hpp" + #include "registries/openbmc_message_registry.hpp" +@@ -511,47 +512,32 @@ class Subscription : public persistent_data::UserSubscription + } + #endif + +- void filterAndSendReports(const std::string& id2, +- const std::string& readingsTs, +- const ReadingsObjType& readings) ++ void filterAndSendReports( ++ const std::string& id, ++ const std::variant<telemetry::TimestampReadings>& var) + { +- std::string metricReportDef = +- "/redfish/v1/TelemetryService/MetricReportDefinitions/" + id2; ++ std::string mrdUri = telemetry::metricReportDefinitionUri + id; + + // Empty list means no filter. Send everything. + if (metricReportDefinitions.size()) + { + if (std::find(metricReportDefinitions.begin(), + metricReportDefinitions.end(), +- metricReportDef) == metricReportDefinitions.end()) ++ mrdUri) == metricReportDefinitions.end()) + { + return; + } + } + +- nlohmann::json metricValuesArray = nlohmann::json::array(); +- for (const auto& it : readings) ++ nlohmann::json msg; ++ if (!telemetry::fillReport(msg, id, var)) + { +- metricValuesArray.push_back({}); +- nlohmann::json& entry = metricValuesArray.back(); +- +- auto& [id, property, value, timestamp] = it; +- +- entry = {{"MetricId", id}, +- {"MetricProperty", property}, +- {"MetricValue", std::to_string(value)}, +- {"Timestamp", crow::utility::getDateTime(timestamp)}}; ++ BMCWEB_LOG_ERROR << "Failed to fill the MetricReport for DBus " ++ "Report with id " ++ << id; ++ return; + } + +- nlohmann::json msg = { +- {"@odata.id", "/redfish/v1/TelemetryService/MetricReports/" + id}, +- {"@odata.type", "#MetricReport.v1_3_0.MetricReport"}, +- {"Id", id2}, +- {"Name", id2}, +- {"Timestamp", readingsTs}, +- {"MetricReportDefinition", {{"@odata.id", metricReportDef}}}, +- {"MetricValues", metricValuesArray}}; +- + this->sendEvent( + msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace)); + } +@@ -1317,75 +1303,6 @@ class EventServiceManager + } + + #endif +- +- void getMetricReading(const std::string& service, +- const std::string& objPath, const std::string& intf) +- { +- std::size_t found = objPath.find_last_of('/'); +- if (found == std::string::npos) +- { +- BMCWEB_LOG_DEBUG << "Invalid objPath received"; +- return; +- } +- +- std::string idStr = objPath.substr(found + 1); +- if (idStr.empty()) +- { +- BMCWEB_LOG_DEBUG << "Invalid ID in objPath"; +- return; +- } +- +- crow::connections::systemBus->async_method_call( +- [idStr{std::move(idStr)}]( +- const boost::system::error_code ec, +- boost::container::flat_map< +- std::string, std::variant<int32_t, ReadingsObjType>>& +- resp) { +- if (ec) +- { +- BMCWEB_LOG_DEBUG +- << "D-Bus call failed to GetAll metric readings."; +- return; +- } +- +- const int32_t* timestampPtr = +- std::get_if<int32_t>(&resp["Timestamp"]); +- if (!timestampPtr) +- { +- BMCWEB_LOG_DEBUG << "Failed to Get timestamp."; +- return; +- } +- +- ReadingsObjType* readingsPtr = +- std::get_if<ReadingsObjType>(&resp["Readings"]); +- if (!readingsPtr) +- { +- BMCWEB_LOG_DEBUG << "Failed to Get Readings property."; +- return; +- } +- +- if (!readingsPtr->size()) +- { +- BMCWEB_LOG_DEBUG << "No metrics report to be transferred"; +- return; +- } +- +- for (const auto& it : +- EventServiceManager::getInstance().subscriptionsMap) +- { +- std::shared_ptr<Subscription> entry = it.second; +- if (entry->eventFormatType == metricReportFormatType) +- { +- entry->filterAndSendReports( +- idStr, crow::utility::getDateTime(*timestampPtr), +- *readingsPtr); +- } +- } +- }, +- service, objPath, "org.freedesktop.DBus.Properties", "GetAll", +- intf); +- } +- + void unregisterMetricReportSignal() + { + if (matchTelemetryMonitor) +@@ -1405,9 +1322,11 @@ class EventServiceManager + } + + BMCWEB_LOG_DEBUG << "Metrics report signal - Register"; +- std::string matchStr( +- "type='signal',member='ReportUpdate', " +- "interface='xyz.openbmc_project.MonitoringService.Report'"); ++ std::string matchStr = "type='signal',member='PropertiesChanged'," ++ "interface='org.freedesktop.DBus.Properties'," ++ "path_namespace=/xyz/openbmc_project/Telemetry/" ++ "Reports/TelemetryService," ++ "arg0=xyz.openbmc_project.Telemetry.Report"; + + matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match::match>( + *crow::connections::systemBus, matchStr, +@@ -1418,10 +1337,43 @@ class EventServiceManager + return; + } + +- std::string service = msg.get_sender(); +- std::string objPath = msg.get_path(); +- std::string intf = msg.get_interface(); +- getMetricReading(service, objPath, intf); ++ sdbusplus::message::object_path path(msg.get_path()); ++ std::string id = path.filename(); ++ if (id.empty()) ++ { ++ BMCWEB_LOG_ERROR << "Failed to get Id from path"; ++ return; ++ } ++ ++ std::string intf; ++ std::vector<std::pair< ++ std::string, std::variant<telemetry::TimestampReadings>>> ++ props; ++ std::vector<std::string> invalidProps; ++ msg.read(intf, props, invalidProps); ++ ++ auto found = ++ std::find_if(props.begin(), props.end(), [](const auto& x) { ++ return x.first == "Readings"; ++ }); ++ if (found == props.end()) ++ { ++ BMCWEB_LOG_INFO ++ << "Failed to get Readings from Report properties"; ++ return; ++ } ++ ++ const std::variant<telemetry::TimestampReadings>& readings = ++ found->second; ++ for (const auto& it : ++ EventServiceManager::getInstance().subscriptionsMap) ++ { ++ Subscription& entry = *it.second.get(); ++ if (entry.eventFormatType == metricReportFormatType) ++ { ++ entry.filterAndSendReports(id, readings); ++ } ++ } + }); + } + +diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp +index 63c8c19..7fe281d 100644 +--- a/redfish-core/lib/metric_report.hpp ++++ b/redfish-core/lib/metric_report.hpp +@@ -33,16 +33,14 @@ inline nlohmann::json toMetricValues(const Readings& readings) + return metricValues; + } + +-inline void fillReport(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, +- const std::string& id, ++inline bool fillReport(nlohmann::json& json, const std::string& id, + const std::variant<TimestampReadings>& var) + { +- asyncResp->res.jsonValue["@odata.type"] = +- "#MetricReport.v1_3_0.MetricReport"; +- asyncResp->res.jsonValue["@odata.id"] = telemetry::metricReportUri + id; +- asyncResp->res.jsonValue["Id"] = id; +- asyncResp->res.jsonValue["Name"] = id; +- asyncResp->res.jsonValue["MetricReportDefinition"]["@odata.id"] = ++ json["@odata.type"] = "#MetricReport.v1_3_0.MetricReport"; ++ json["@odata.id"] = telemetry::metricReportUri + id; ++ json["Id"] = id; ++ json["Name"] = id; ++ json["MetricReportDefinition"]["@odata.id"] = + telemetry::metricReportDefinitionUri + id; + + const TimestampReadings* timestampReadings = +@@ -50,14 +48,14 @@ inline void fillReport(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + if (!timestampReadings) + { + BMCWEB_LOG_ERROR << "Property type mismatch or property is missing"; +- messages::internalError(asyncResp->res); +- return; ++ return false; + } + + const auto& [timestamp, readings] = *timestampReadings; +- asyncResp->res.jsonValue["Timestamp"] = ++ json["Timestamp"] = + crow::utility::getDateTime(static_cast<time_t>(timestamp)); +- asyncResp->res.jsonValue["MetricValues"] = toMetricValues(readings); ++ json["MetricValues"] = toMetricValues(readings); ++ return true; + } + } // namespace telemetry + +@@ -118,7 +116,11 @@ inline void requestRoutesMetricReport(App& app) + return; + } + +- telemetry::fillReport(asyncResp, id, ret); ++ if (!telemetry::fillReport( ++ asyncResp->res.jsonValue, id, ret)) ++ { ++ messages::internalError(asyncResp->res); ++ } + }, + telemetry::service, reportPath, + "org.freedesktop.DBus.Properties", "Get", +-- +2.25.1 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Switched-bmcweb-to-use-new-telemetry-service-API.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Switched-bmcweb-to-use-new-telemetry-service-API.patch new file mode 100644 index 000000000..5dd2f51bc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Switched-bmcweb-to-use-new-telemetry-service-API.patch @@ -0,0 +1,301 @@ +From 8ba1bcc3503cafb33b1a06356d4f8f92ae23e39a Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Thu, 17 Jun 2021 13:37:57 +0000 +Subject: [PATCH] Switched bmcweb to use new telemetry service API + +Added support for multiple MetricProperties. Added support for new +parameters: CollectionTimeScope, CollectionDuration. + +Tested: + - It is possible to create MetricReportDefinitions with multiple + MetricProperties. + - Stub values for new parameters are correctly passed to telemetry + service. + - All existing telemetry service functionalities remain unchanged. + +Change-Id: I2cd17069e3ea015c8f5571c29278f1d50536272a +Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +--- + redfish-core/lib/metric_report_definition.hpp | 212 ++++++++++-------- + 1 file changed, 114 insertions(+), 98 deletions(-) + +diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp +index a0c4f1d..7c26787 100644 +--- a/redfish-core/lib/metric_report_definition.hpp ++++ b/redfish-core/lib/metric_report_definition.hpp +@@ -7,6 +7,8 @@ + #include <app.hpp> + #include <boost/container/flat_map.hpp> + #include <registries/privilege_registry.hpp> ++#include <sdbusplus/asio/property.hpp> ++#include <sdbusplus/unpack_properties.hpp> + + #include <tuple> + #include <variant> +@@ -17,87 +19,90 @@ namespace redfish + namespace telemetry + { + +-using ReadingParameters = +- std::vector<std::tuple<sdbusplus::message::object_path, std::string, +- std::string, std::string>>; ++using ReadingParameters = std::vector< ++ std::tuple<std::vector<sdbusplus::message::object_path>, std::string, ++ std::string, std::string, std::string, uint64_t>>; + + inline void fillReportDefinition( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id, + const std::vector< +- std::pair<std::string, std::variant<std::string, bool, uint64_t, +- ReadingParameters>>>& ret) ++ std::pair<std::string, std::variant<std::monostate, std::string, bool, ++ uint64_t, ReadingParameters>>>& ++ properties) + { +- asyncResp->res.jsonValue["@odata.type"] = +- "#MetricReportDefinition.v1_3_0.MetricReportDefinition"; +- asyncResp->res.jsonValue["@odata.id"] = +- telemetry::metricReportDefinitionUri + id; +- asyncResp->res.jsonValue["Id"] = id; +- asyncResp->res.jsonValue["Name"] = id; +- asyncResp->res.jsonValue["MetricReport"]["@odata.id"] = +- telemetry::metricReportUri + id; +- asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; +- asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite"; +- +- const bool* emitsReadingsUpdate = nullptr; +- const bool* logToMetricReportsCollection = nullptr; +- const ReadingParameters* readingParams = nullptr; +- const std::string* reportingType = nullptr; +- const uint64_t* interval = nullptr; +- for (const auto& [key, var] : ret) ++ try + { +- if (key == "EmitsReadingsUpdate") ++ bool emitsReadingsUpdate = false; ++ bool logToMetricReportsCollection = false; ++ ReadingParameters readingParams; ++ std::string reportingType; ++ uint64_t interval = 0u; ++ ++ sdbusplus::unpackProperties( ++ properties, "EmitsReadingsUpdate", emitsReadingsUpdate, ++ "LogToMetricReportsCollection", logToMetricReportsCollection, ++ "ReadingParametersFutureVersion", readingParams, "ReportingType", ++ reportingType, "Interval", interval); ++ ++ std::vector<std::string> redfishReportActions; ++ redfishReportActions.reserve(2); ++ if (emitsReadingsUpdate) + { +- emitsReadingsUpdate = std::get_if<bool>(&var); ++ redfishReportActions.emplace_back("RedfishEvent"); + } +- else if (key == "LogToMetricReportsCollection") ++ if (logToMetricReportsCollection) + { +- logToMetricReportsCollection = std::get_if<bool>(&var); ++ redfishReportActions.emplace_back("LogToMetricReportsCollection"); + } +- else if (key == "ReadingParameters") +- { +- readingParams = std::get_if<ReadingParameters>(&var); +- } +- else if (key == "ReportingType") +- { +- reportingType = std::get_if<std::string>(&var); +- } +- else if (key == "Interval") ++ ++ nlohmann::json metrics = nlohmann::json::array(); ++ for (auto& [sensorPath, operationType, id, metadata, ++ collectionTimeScope, collectionDuration] : readingParams) + { +- interval = std::get_if<uint64_t>(&var); ++ std::vector<std::string> metricProperties; ++ ++ nlohmann::json parsedMetadata = nlohmann::json::parse(metadata); ++ if (!json_util::readJson(parsedMetadata, asyncResp->res, ++ "MetricProperties", metricProperties)) ++ { ++ BMCWEB_LOG_ERROR << "Failed to read metadata"; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ metrics.push_back({ ++ {"MetricId", id}, ++ {"MetricProperties", std::move(metricProperties)}, ++ }); + } +- } +- if (!emitsReadingsUpdate || !logToMetricReportsCollection || +- !readingParams || !reportingType || !interval) +- { +- BMCWEB_LOG_ERROR << "Property type mismatch or property is missing"; +- messages::internalError(asyncResp->res); +- return; +- } + +- std::vector<std::string> redfishReportActions; +- redfishReportActions.reserve(2); +- if (*emitsReadingsUpdate) +- { +- redfishReportActions.emplace_back("RedfishEvent"); ++ asyncResp->res.jsonValue["@odata.type"] = ++ "#MetricReportDefinition.v1_3_0.MetricReportDefinition"; ++ asyncResp->res.jsonValue["@odata.id"] = ++ telemetry::metricReportDefinitionUri + id; ++ asyncResp->res.jsonValue["Id"] = id; ++ asyncResp->res.jsonValue["Name"] = id; ++ asyncResp->res.jsonValue["MetricReport"]["@odata.id"] = ++ telemetry::metricReportUri + id; ++ asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; ++ asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite"; ++ asyncResp->res.jsonValue["Metrics"] = metrics; ++ asyncResp->res.jsonValue["MetricReportDefinitionType"] = reportingType; ++ asyncResp->res.jsonValue["ReportActions"] = redfishReportActions; ++ asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] = ++ time_utils::toDurationString(std::chrono::milliseconds(interval)); + } +- if (*logToMetricReportsCollection) ++ catch (const sdbusplus::exception::UnpackPropertyError& error) + { +- redfishReportActions.emplace_back("LogToMetricReportsCollection"); ++ BMCWEB_LOG_ERROR << error.what() << ", property: " ++ << error.propertyName + ", reason: " << error.reason; ++ messages::internalError(asyncResp->res); + } +- +- nlohmann::json metrics = nlohmann::json::array(); +- for (auto& [sensorPath, operationType, id, metadata] : *readingParams) ++ catch (const nlohmann::json::parse_error& e) + { +- metrics.push_back({ +- {"MetricId", id}, +- {"MetricProperties", {metadata}}, +- }); ++ BMCWEB_LOG_ERROR << "Failed to parse metadata: " << e.what(); ++ messages::internalError(asyncResp->res); + } +- asyncResp->res.jsonValue["Metrics"] = metrics; +- asyncResp->res.jsonValue["MetricReportDefinitionType"] = *reportingType; +- asyncResp->res.jsonValue["ReportActions"] = redfishReportActions; +- asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] = +- time_utils::toDurationString(std::chrono::milliseconds(*interval)); + } + + struct AddReportArgs +@@ -275,6 +280,11 @@ class AddReport + + for (const auto& [id, uris] : args.metrics) + { ++ std::vector<sdbusplus::message::object_path> dbusPaths; ++ dbusPaths.reserve(uris.size()); ++ nlohmann::json metadata; ++ metadata["MetricProperties"] = nlohmann::json::array(); ++ + for (size_t i = 0; i < uris.size(); i++) + { + const std::string& uri = uris[i]; +@@ -291,8 +301,12 @@ class AddReport + } + + const std::string& dbusPath = el->second; +- readingParams.emplace_back(dbusPath, "SINGLE", id, uri); ++ dbusPaths.emplace_back(dbusPath); ++ metadata["MetricProperties"].emplace_back(uri); + } ++ ++ readingParams.emplace_back(dbusPaths, "SINGLE", id, metadata.dump(), ++ "Point", 0u); + } + const std::shared_ptr<bmcweb::AsyncResp> aResp = asyncResp; + crow::connections::systemBus->async_method_call( +@@ -330,10 +344,10 @@ class AddReport + messages::created(aResp->res); + }, + telemetry::service, "/xyz/openbmc_project/Telemetry/Reports", +- "xyz.openbmc_project.Telemetry.ReportManager", "AddReport", +- "TelemetryService/" + args.name, args.reportingType, +- args.emitsReadingsUpdate, args.logToMetricReportsCollection, +- args.interval, readingParams); ++ "xyz.openbmc_project.Telemetry.ReportManager", ++ "AddReportFutureVersion", "TelemetryService/" + args.name, ++ args.reportingType, args.emitsReadingsUpdate, ++ args.logToMetricReportsCollection, args.interval, readingParams); + } + + void insert(const boost::container::flat_map<std::string, std::string>& el) +@@ -415,37 +429,39 @@ inline void requestRoutesMetricReportDefinition(App& app) + BMCWEB_ROUTE(app, + "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") + .privileges(redfish::privileges::getMetricReportDefinition) +- .methods(boost::beast::http::verb::get)( +- [](const crow::Request&, +- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, +- const std::string& id) { +- crow::connections::systemBus->async_method_call( +- [asyncResp, id]( +- const boost::system::error_code ec, +- const std::vector<std::pair< +- std::string, +- std::variant<std::string, bool, uint64_t, +- telemetry::ReadingParameters>>>& ret) { +- if (ec.value() == EBADR || +- ec == boost::system::errc::host_unreachable) +- { +- messages::resourceNotFound( +- asyncResp->res, "MetricReportDefinition", id); +- return; +- } +- if (ec) +- { +- BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; +- messages::internalError(asyncResp->res); +- return; +- } ++ .methods( ++ boost::beast::http::verb::get)([](const crow::Request&, ++ const std::shared_ptr< ++ bmcweb::AsyncResp>& asyncResp, ++ const std::string& id) { ++ sdbusplus::asio::getAllProperties( ++ *crow::connections::systemBus, telemetry::service, ++ telemetry::getDbusReportPath(id), telemetry::reportInterface, ++ [asyncResp, ++ id](boost::system::error_code ec, ++ const std::vector<std::pair< ++ std::string, ++ std::variant<std::monostate, std::string, bool, ++ uint64_t, telemetry::ReadingParameters>>>& ++ properties) { ++ if (ec.value() == EBADR || ++ ec == boost::system::errc::host_unreachable) ++ { ++ messages::resourceNotFound( ++ asyncResp->res, "MetricReportDefinition", id); ++ return; ++ } ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ telemetry::fillReportDefinition(asyncResp, id, properties); ++ }); ++ }); + +- telemetry::fillReportDefinition(asyncResp, id, ret); +- }, +- telemetry::service, telemetry::getDbusReportPath(id), +- "org.freedesktop.DBus.Properties", "GetAll", +- telemetry::reportInterface); +- }); + BMCWEB_ROUTE(app, + "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") + .privileges(redfish::privileges::deleteMetricReportDefinitionCollection) +-- +2.25.1 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-MetricDefinition-property-in-MetricReport.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-MetricDefinition-property-in-MetricReport.patch new file mode 100644 index 000000000..bf5a09d9d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-MetricDefinition-property-in-MetricReport.patch @@ -0,0 +1,268 @@ +From dab3c96f9e39a89d7c359e22655650c7c16952ec Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Tue, 12 Oct 2021 08:06:13 +0000 +Subject: [PATCH] Add support for MetricDefinition property in MetricReport + +Added MetricDefinition as part of MetricValues array returned by +MetricReport. It contains single @odata.id with URI to proper +MetricDefinition resource - depending on MetricProperty. + +Testing done: +- GET request on redfish/v1/TelemetryService/MetricReports + got response with MetricDefinition and proper id inside + MetricValues array. + +Testing steps: +1. POST on redfish/v1/TelemetryService/MetricReportDefinitions + with body: +{ + "Id": "PeriodicReport_1", + "MetricReportDefinitionType": "Periodic", + "ReportActions": [ + "LogToMetricReportsCollection", + "RedfishEvent" + ], + "Metrics": [ + { + "MetricId": "sensor_1", + "MetricProperties": [ + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/1/Reading" + ] + } + ], + "Schedule": { + "RecurrenceInterval": "PT10S" + } +} + +2. GET on redfish/v1/TelemetryService/MetricReports/PeriodicReport_1 + should return: +{ + "@odata.id": + "/redfish/v1/TelemetryService/MetricReports/PeriodicReport_1", + "@odata.type": "#MetricReport.v1_3_0.MetricReport", + "Id": "PeriodicReport_1", + "MetricReportDefinition": { + "@odata.id": + "/redfish/v1/TelemetryService/MetricReportDefinitions/PeriodicReport_1" + }, + "MetricValues": [ + { + "MetricDefinition": { + "@odata.id": + "/redfish/v1/TelemetryService/MetricDefinitions/Rotational" + }, + "MetricId": "sensor_1", + "MetricProperty": + "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/1/Reading", + "MetricValue": "nan", + "Timestamp": "1970-01-01T00:03:21+00:00" + } + ], + "Name": "PeriodicReport_1", + "Timestamp": "1970-01-01T00:03:21+00:00" +} + +Change-Id: I7181c612f9b443015d551259bae25303aa436822 +Signed-off-by: Szymon Dompke <szymon.dompke@intel.com> +--- + meson.build | 4 +- + .../include/utils/telemetry_utils.hpp | 40 ++++++++++++ + redfish-core/lib/metric_report.hpp | 64 +++++++++++++++---- + redfish-core/lib/sensors.hpp | 2 + + 4 files changed, 95 insertions(+), 15 deletions(-) + +diff --git a/meson.build b/meson.build +index 6b6a8ab..218ea49 100644 +--- a/meson.build ++++ b/meson.build +@@ -377,6 +377,8 @@ srcfiles_unittest = [ + 'http/ut/utility_test.cpp' + ] + ++srcfiles_unittest_dependencies = ['redfish-core/src/error_messages.cpp', 'src/boost_url.cpp'] ++ + # Gather the Configuration data + + conf_data = configuration_data() +@@ -434,7 +436,7 @@ executable('bmcweb',srcfiles_bmcweb, + if(get_option('tests').enabled()) + foreach src_test : srcfiles_unittest + testname = src_test.split('/')[-1].split('.')[0] +- test(testname,executable(testname,src_test, ++ test(testname,executable(testname,[src_test] + srcfiles_unittest_dependencies, + include_directories : incdir, + install_dir: bindir, + dependencies: [ +diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp +index 1b4f75d..c0c5ba3 100644 +--- a/redfish-core/include/utils/telemetry_utils.hpp ++++ b/redfish-core/include/utils/telemetry_utils.hpp +@@ -17,6 +17,46 @@ constexpr const char* metricReportDefinitionUri = + constexpr const char* metricReportUri = + "/redfish/v1/TelemetryService/MetricReports/"; + ++inline std::optional<nlohmann::json> ++ getMetadataJson(const std::string& metadataStr) ++{ ++ std::optional<nlohmann::json> res = ++ nlohmann::json::parse(metadataStr, nullptr, false); ++ if (res->is_discarded()) ++ { ++ BMCWEB_LOG_ERROR << "Malformed reading metatadata JSON provided by " ++ "telemetry service."; ++ return std::nullopt; ++ } ++ return res; ++} ++ ++inline std::optional<std::string> ++ readStringFromMetadata(const nlohmann::json& metadataJson, const char* key) ++{ ++ std::optional<std::string> res; ++ if (auto it = metadataJson.find(key); it != metadataJson.end()) ++ { ++ if (const std::string* value = it->get_ptr<const std::string*>()) ++ { ++ res = *value; ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "Incorrect reading metatadata JSON provided by " ++ "telemetry service. Missing key '" ++ << key << "'."; ++ } ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "Incorrect reading metatadata JSON provided by " ++ "telemetry service. Key '" ++ << key << "' has a wrong type."; ++ } ++ return res; ++} ++ + inline void + getReportCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& uri) +diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp +index 7fe281d..13bf792 100644 +--- a/redfish-core/lib/metric_report.hpp ++++ b/redfish-core/lib/metric_report.hpp +@@ -1,5 +1,6 @@ + #pragma once + ++#include "sensors.hpp" + #include "utils/telemetry_utils.hpp" + + #include <app.hpp> +@@ -15,34 +16,56 @@ using Readings = + std::vector<std::tuple<std::string, std::string, double, uint64_t>>; + using TimestampReadings = std::tuple<uint64_t, Readings>; + +-inline nlohmann::json toMetricValues(const Readings& readings) ++inline bool fillMetricValues(nlohmann::json& metricValues, ++ const Readings& readings) + { +- nlohmann::json metricValues = nlohmann::json::array_t(); +- +- for (auto& [id, metadata, sensorValue, timestamp] : readings) ++ for (auto& [id, metadataStr, sensorValue, timestamp] : readings) + { ++ std::optional<nlohmann::json> readingMetadataJson = ++ getMetadataJson(metadataStr); ++ if (!readingMetadataJson) ++ { ++ return false; ++ } ++ ++ std::optional<std::string> sensorDbusPath = ++ readStringFromMetadata(*readingMetadataJson, "SensorDbusPath"); ++ if (!sensorDbusPath) ++ { ++ return false; ++ } ++ ++ std::optional<std::string> sensorRedfishUri = ++ readStringFromMetadata(*readingMetadataJson, "SensorRedfishUri"); ++ if (!sensorRedfishUri) ++ { ++ return false; ++ } ++ ++ std::string metricDefinition = ++ std::string(metricDefinitionUri) + ++ sensors::toReadingType( ++ sdbusplus::message::object_path(*sensorDbusPath) ++ .parent_path() ++ .filename()); ++ + metricValues.push_back({ ++ {"MetricDefinition", ++ nlohmann::json{{"@odata.id", metricDefinition}}}, + {"MetricId", id}, +- {"MetricProperty", metadata}, ++ {"MetricProperty", *sensorRedfishUri}, + {"MetricValue", std::to_string(sensorValue)}, + {"Timestamp", + crow::utility::getDateTime(static_cast<time_t>(timestamp))}, + }); + } + +- return metricValues; ++ return true; + } + + inline bool fillReport(nlohmann::json& json, const std::string& id, + const std::variant<TimestampReadings>& var) + { +- json["@odata.type"] = "#MetricReport.v1_3_0.MetricReport"; +- json["@odata.id"] = telemetry::metricReportUri + id; +- json["Id"] = id; +- json["Name"] = id; +- json["MetricReportDefinition"]["@odata.id"] = +- telemetry::metricReportDefinitionUri + id; +- + const TimestampReadings* timestampReadings = + std::get_if<TimestampReadings>(&var); + if (!timestampReadings) +@@ -52,9 +75,22 @@ inline bool fillReport(nlohmann::json& json, const std::string& id, + } + + const auto& [timestamp, readings] = *timestampReadings; ++ nlohmann::json metricValues = nlohmann::json::array(); ++ if (!fillMetricValues(metricValues, readings)) ++ { ++ return false; ++ } ++ ++ json["@odata.type"] = "#MetricReport.v1_3_0.MetricReport"; ++ json["@odata.id"] = telemetry::metricReportUri + id; ++ json["Id"] = id; ++ json["Name"] = id; ++ json["MetricReportDefinition"]["@odata.id"] = ++ telemetry::metricReportDefinitionUri + id; + json["Timestamp"] = + crow::utility::getDateTime(static_cast<time_t>(timestamp)); +- json["MetricValues"] = toMetricValues(readings); ++ json["MetricValues"] = metricValues; ++ + return true; + } + } // namespace telemetry +diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp +index 7405e5a..9850b24 100644 +--- a/redfish-core/lib/sensors.hpp ++++ b/redfish-core/lib/sensors.hpp +@@ -21,6 +21,8 @@ + #include <boost/container/flat_map.hpp> + #include <boost/range/algorithm/replace_copy_if.hpp> + #include <dbus_singleton.hpp> ++#include <dbus_utility.hpp> ++#include <error_messages.hpp> + #include <registries/privilege_registry.hpp> + #include <utils/json_utils.hpp> + +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-GET-method-for-TriggerCollection.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-GET-method-for-TriggerCollection.patch new file mode 100644 index 000000000..0646aba5c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-GET-method-for-TriggerCollection.patch @@ -0,0 +1,313 @@ +From a1e89d356ba5ed594a1494efe8257946e1062396 Mon Sep 17 00:00:00 2001 +From: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com> +Date: Tue, 31 Aug 2021 14:35:31 +0200 +Subject: [PATCH] Add GET method for TriggerCollection + +Added GET method for retrieving list of Triggers from Telemetry service + +Tested: +- Added single Trigger and requested result from bmcweb via + /redfish/v1/TelemetryService/Triggers +- Added multiple Triggers numeric and discrete, and requested results + from bmcweb via /redfish/v1/TelemetryService/Triggers +- Verified uri /redfish/v1/TelemetryService/Triggers by using + Redfish-Service-Validator (all passed) + +Signed-off-by: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com> +Change-Id: Ide00eb44901ea1b97b80fc5c5ddfd97e393d4a04 +--- + redfish-core/include/redfish.hpp | 2 + + .../include/utils/telemetry_utils.hpp | 40 ++++++++--- + redfish-core/lib/metric_report.hpp | 6 +- + redfish-core/lib/metric_report_definition.hpp | 6 +- + redfish-core/lib/trigger.hpp | 31 ++++++++ + scripts/update_schemas.py | 1 + + static/redfish/v1/$metadata/index.xml | 3 + + .../v1/schema/TriggersCollection_v1.xml | 70 +++++++++++++++++++ + 8 files changed, 144 insertions(+), 15 deletions(-) + create mode 100644 redfish-core/lib/trigger.hpp + create mode 100644 static/redfish/v1/schema/TriggersCollection_v1.xml + +diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp +index 9fb0ffe..99b3fe6 100644 +--- a/redfish-core/include/redfish.hpp ++++ b/redfish-core/include/redfish.hpp +@@ -42,6 +42,7 @@ + #include "../lib/task.hpp" + #include "../lib/telemetry_service.hpp" + #include "../lib/thermal.hpp" ++#include "../lib/trigger.hpp" + #include "../lib/update_service.hpp" + #include "../lib/virtual_media.hpp" + +@@ -197,6 +198,7 @@ class RedfishService + + hypervisor::requestRoutesHypervisorSystems(app); + ++ requestRoutesTriggerCollection(app); + requestRoutesTelemetryService(app); + requestRoutesMetricReportDefinitionCollection(app); + requestRoutesMetricReportDefinition(app); +diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp +index c0c5ba3..df1aa68 100644 +--- a/redfish-core/include/utils/telemetry_utils.hpp ++++ b/redfish-core/include/utils/telemetry_utils.hpp +@@ -9,6 +9,8 @@ namespace telemetry + { + + constexpr const char* service = "xyz.openbmc_project.Telemetry"; ++constexpr const char* reportSubtree = ++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService"; + constexpr const char* reportInterface = "xyz.openbmc_project.Telemetry.Report"; + constexpr const char* metricDefinitionUri = + "/redfish/v1/TelemetryService/MetricDefinitions/"; +@@ -16,6 +18,11 @@ constexpr const char* metricReportDefinitionUri = + "/redfish/v1/TelemetryService/MetricReportDefinitions/"; + constexpr const char* metricReportUri = + "/redfish/v1/TelemetryService/MetricReports/"; ++constexpr const char* triggerSubtree = ++ "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService"; ++constexpr const char* triggerInterface = ++ "xyz.openbmc_project.Telemetry.Trigger"; ++constexpr const char* triggerUri = "/redfish/v1/TelemetryService/Triggers/"; + + inline std::optional<nlohmann::json> + getMetadataJson(const std::string& metadataStr) +@@ -57,15 +64,27 @@ inline std::optional<std::string> + return res; + } + +-inline void +- getReportCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, +- const std::string& uri) ++struct CollectionParams + { +- const std::array<const char*, 1> interfaces = {reportInterface}; ++ const char* subtree; ++ int depth; ++ std::array<const char*, 1> interfaces; + ++ CollectionParams() = delete; ++ CollectionParams(const char* st, int dp, ++ const std::array<const char*, 1>& ifaces) : ++ subtree{st}, ++ depth{dp}, interfaces{ifaces} ++ {} ++}; ++ ++inline void getCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ const std::string& uri, ++ const CollectionParams& params) ++{ + crow::connections::systemBus->async_method_call( + [asyncResp, uri](const boost::system::error_code ec, +- const std::vector<std::string>& reports) { ++ const std::vector<std::string>& items) { + if (ec == boost::system::errc::io_error) + { + asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); +@@ -82,13 +101,13 @@ inline void + nlohmann::json& members = asyncResp->res.jsonValue["Members"]; + members = nlohmann::json::array(); + +- for (const std::string& report : reports) ++ for (const std::string& item : items) + { +- sdbusplus::message::object_path path(report); ++ sdbusplus::message::object_path path(item); + std::string name = path.filename(); + if (name.empty()) + { +- BMCWEB_LOG_ERROR << "Received invalid path: " << report; ++ BMCWEB_LOG_ERROR << "Received invalid path: " << item; + messages::internalError(asyncResp->res); + return; + } +@@ -99,9 +118,8 @@ inline void + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", +- "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", +- "/xyz/openbmc_project/Telemetry/Reports/TelemetryService", 1, +- interfaces); ++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", params.subtree, ++ params.depth, params.interfaces); + } + + inline std::string getDbusReportPath(const std::string& id) +diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp +index 13bf792..ea4cd62 100644 +--- a/redfish-core/lib/metric_report.hpp ++++ b/redfish-core/lib/metric_report.hpp +@@ -108,8 +108,10 @@ inline void requestRoutesMetricReportCollection(App& app) + "/redfish/v1/TelemetryService/MetricReports"; + asyncResp->res.jsonValue["Name"] = "Metric Report Collection"; + +- telemetry::getReportCollection(asyncResp, +- telemetry::metricReportUri); ++ telemetry::getCollection( ++ asyncResp, telemetry::metricReportUri, ++ telemetry::CollectionParams(telemetry::reportSubtree, 1, ++ {telemetry::reportInterface})); + }); + } + +diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp +index 7c26787..c97a1df 100644 +--- a/redfish-core/lib/metric_report_definition.hpp ++++ b/redfish-core/lib/metric_report_definition.hpp +@@ -377,8 +377,10 @@ inline void requestRoutesMetricReportDefinitionCollection(App& app) + asyncResp->res.jsonValue["Name"] = + "Metric Definition Collection"; + +- telemetry::getReportCollection( +- asyncResp, telemetry::metricReportDefinitionUri); ++ telemetry::getCollection( ++ asyncResp, telemetry::metricReportDefinitionUri, ++ telemetry::CollectionParams(telemetry::reportSubtree, 1, ++ {telemetry::reportInterface})); + }); + + BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") +diff --git a/redfish-core/lib/trigger.hpp b/redfish-core/lib/trigger.hpp +new file mode 100644 +index 0000000..681b3b4 +--- /dev/null ++++ b/redfish-core/lib/trigger.hpp +@@ -0,0 +1,31 @@ ++#pragma once ++ ++#include "utils/telemetry_utils.hpp" ++ ++#include <app.hpp> ++#include <registries/privilege_registry.hpp> ++ ++namespace redfish ++{ ++ ++inline void requestRoutesTriggerCollection(App& app) ++{ ++ BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/") ++ .privileges(redfish::privileges::getTriggersCollection) ++ .methods(boost::beast::http::verb::get)( ++ [](const crow::Request&, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ asyncResp->res.jsonValue["@odata.type"] = ++ "#TriggersCollection.TriggersCollection"; ++ asyncResp->res.jsonValue["@odata.id"] = ++ "/redfish/v1/TelemetryService/Triggers"; ++ asyncResp->res.jsonValue["Name"] = "Triggers Collection"; ++ ++ telemetry::getCollection( ++ asyncResp, telemetry::triggerUri, ++ telemetry::CollectionParams(telemetry::triggerSubtree, 1, ++ {telemetry::triggerInterface})); ++ }); ++} ++ ++} // namespace redfish +diff --git a/scripts/update_schemas.py b/scripts/update_schemas.py +index dd39278..d66a59a 100755 +--- a/scripts/update_schemas.py ++++ b/scripts/update_schemas.py +@@ -93,6 +93,7 @@ include_list = [ + 'TaskService', + 'TelemetryService', + 'Thermal', ++ 'TriggersCollection', + 'UpdateService', + 'VLanNetworkInterfaceCollection', + 'VLanNetworkInterface', +diff --git a/static/redfish/v1/$metadata/index.xml b/static/redfish/v1/$metadata/index.xml +index 876ebfb..75e3dd4 100644 +--- a/static/redfish/v1/$metadata/index.xml ++++ b/static/redfish/v1/$metadata/index.xml +@@ -2215,6 +2215,9 @@ + <edmx:Include Namespace="Thermal.v1_7_0"/> + <edmx:Include Namespace="Thermal.v1_7_1"/> + </edmx:Reference> ++ <edmx:Reference Uri="/redfish/v1/schema/TriggersCollection_v1.xml"> ++ <edmx:Include Namespace="TriggersCollection"/> ++ </edmx:Reference> + <edmx:Reference Uri="/redfish/v1/schema/UpdateService_v1.xml"> + <edmx:Include Namespace="UpdateService"/> + <edmx:Include Namespace="UpdateService.v1_0_0"/> +diff --git a/static/redfish/v1/schema/TriggersCollection_v1.xml b/static/redfish/v1/schema/TriggersCollection_v1.xml +new file mode 100644 +index 0000000..399bebd +--- /dev/null ++++ b/static/redfish/v1/schema/TriggersCollection_v1.xml +@@ -0,0 +1,70 @@ ++<?xml version="1.0" encoding="UTF-8"?> ++<!----> ++<!--################################################################################ --> ++<!--# Redfish Schema: TriggerSetCollection --> ++<!--# --> ++<!--# For a detailed change log, see the README file contained in the DSP8010 bundle, --> ++<!--# available at http://www.dmtf.org/standards/redfish --> ++<!--# Copyright 2014-2021 DMTF. --> ++<!--# For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright --> ++<!--################################################################################ --> ++<!----> ++<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0"> ++ ++ <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml"> ++ <edmx:Include Namespace="Org.OData.Core.V1" Alias="OData"/> ++ </edmx:Reference> ++ <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Capabilities.V1.xml"> ++ <edmx:Include Namespace="Org.OData.Capabilities.V1" Alias="Capabilities"/> ++ </edmx:Reference> ++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Resource_v1.xml"> ++ <edmx:Include Namespace="Resource.v1_0_0"/> ++ </edmx:Reference> ++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/RedfishExtensions_v1.xml"> ++ <edmx:Include Namespace="RedfishExtensions.v1_0_0" Alias="Redfish"/> ++ </edmx:Reference> ++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Triggers_v1.xml"> ++ <edmx:Include Namespace="Triggers"/> ++ </edmx:Reference> ++ ++ <edmx:DataServices> ++ ++ <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="TriggersCollection"> ++ <Annotation Term="Redfish.OwningEntity" String="DMTF"/> ++ ++ <EntityType Name="TriggersCollection" BaseType="Resource.v1_0_0.ResourceCollection"> ++ <Annotation Term="OData.Description" String="The collection of Triggers resource instances."/> ++ <Annotation Term="OData.LongDescription" String="This resource shall represent a resource collection of Triggers instances for a Redfish implementation."/> ++ <Annotation Term="Capabilities.InsertRestrictions"> ++ <Record> ++ <PropertyValue Property="Insertable" Bool="true"/> ++ <Annotation Term="OData.Description" String="Create triggers through a POST to the trigger collection."/> ++ </Record> ++ </Annotation> ++ <Annotation Term="Capabilities.UpdateRestrictions"> ++ <Record> ++ <PropertyValue Property="Updatable" Bool="false"/> ++ </Record> ++ </Annotation> ++ <Annotation Term="Capabilities.DeleteRestrictions"> ++ <Record> ++ <PropertyValue Property="Deletable" Bool="false"/> ++ </Record> ++ </Annotation> ++ <Annotation Term="Redfish.Uris"> ++ <Collection> ++ <String>/redfish/v1/TelemetryService/Triggers</String> ++ </Collection> ++ </Annotation> ++ <NavigationProperty Name="Members" Type="Collection(Triggers.Triggers)"> ++ <Annotation Term="OData.Permissions" EnumMember="OData.Permission/Read"/> ++ <Annotation Term="OData.Description" String="The members of this collection."/> ++ <Annotation Term="OData.LongDescription" String="This property shall contain an array of links to the members of this collection."/> ++ <Annotation Term="OData.AutoExpandReferences"/> ++ <Annotation Term="Redfish.Required"/> ++ </NavigationProperty> ++ </EntityType> ++ ++ </Schema> ++ </edmx:DataServices> ++</edmx:Edmx> +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Revert-Remove-LogService-from-TelemetryService.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Revert-Remove-LogService-from-TelemetryService.patch new file mode 100644 index 000000000..a80ac61c7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Revert-Remove-LogService-from-TelemetryService.patch @@ -0,0 +1,26 @@ +From da575aaf0bdcb15be261d58314cf7bbbcd92dd74 Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Tue, 12 Oct 2021 08:08:06 +0000 +Subject: [PATCH] Revert "Remove LogService from TelemetryService" + +This reverts commit 2b3da45876aac57a36d3093379a992d699e7e396. +--- + redfish-core/lib/telemetry_service.hpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp +index 027b51b..49471fe 100644 +--- a/redfish-core/lib/telemetry_service.hpp ++++ b/redfish-core/lib/telemetry_service.hpp +@@ -24,6 +24,8 @@ inline void handleTelemetryServiceGet( + "/redfish/v1/TelemetryService/MetricReports"; + asyncResp->res.jsonValue["MetricDefinitions"]["@odata.id"] = + "/redfish/v1/TelemetryService/MetricDefinitions"; ++ asyncResp->res.jsonValue["LogService"]["@odata.id"] = ++ "/redfish/v1/Managers/bmc/LogServices/Journal"; + + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec, +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0007-event-service-fix-added-Context-field-to-response.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0007-event-service-fix-added-Context-field-to-response.patch new file mode 100644 index 000000000..ffab743f6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0007-event-service-fix-added-Context-field-to-response.patch @@ -0,0 +1,29 @@ +From 0ca8c383db8c9afbce63380955a20ada0acc20b7 Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Wed, 2 Jun 2021 12:44:43 +0000 +Subject: [PATCH] event service fix, added Context field to response + +Tested: + - Context field is present + - No regression detected + +Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +--- + redfish-core/include/event_service_manager.hpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 2b957ea..289886b 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -556,6 +556,7 @@ class Subscription + << id; + return; + } ++ msg["Context"] = customText; + + this->sendEvent( + msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace)); +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0009-Add-support-for-deleting-terminated-subscriptions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0009-Add-support-for-deleting-terminated-subscriptions.patch new file mode 100644 index 000000000..548e3d9c6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0009-Add-support-for-deleting-terminated-subscriptions.patch @@ -0,0 +1,46 @@ +From ef83a4fb14648edc6c8370363ff88fb6f060a43b Mon Sep 17 00:00:00 2001 +From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +Date: Mon, 20 Sep 2021 21:55:57 +0530 +Subject: [PATCH] Add support for deleting terminated subscriptions + +Added functionality to delete/remove event subscription(s) which are +configured to Terminate after retries. + +Currently, when an Event is subscribed with Retry Policy as +"TerminateAfterRetries", the state of the connection is set to +"Terminated" after retrying, but the Subscription is not removed. +This commit adds the functionality to detect terminated connection and +remove the respective subscription. + +This commit adds this check for metric reports. + +Tested: + - Created a Subscription with + DeliveryRetryPolicy: "TerminateAfterRetries" + - Received Events successfully on Event listener + - Once the Event listener was stopped, the Subscription was + removed/deleted after retries. + +Change-Id: I3cb0af5bc24411cddcdb3d1d9de25e8e9144106c +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + redfish-core/include/event_service_manager.hpp | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index c9e2812..c2fefb3 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -1535,6 +1535,9 @@ class EventServiceManager + + std::variant<telemetry::TimestampReadings>& readings = + found->second; ++ ++ this->deleteTerminatedSubcriptions(); ++ + for (const auto& it : + EventServiceManager::getInstance().subscriptionsMap) + { +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README new file mode 100644 index 000000000..90916ecec --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README @@ -0,0 +1,30 @@ +These patches are mirror of upstream TelemetryService implementation. +Until change is integrated they will be manually merged here to enable feature in Intel builds. + +Current revisions: +- Add support for MetricDefinition scheme + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/102 + +- Sync Telmetry service with EventService + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/38798/53 + +- Switched bmcweb to use new telemetry service API + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/44270/19 + +- Add support for MetricDefinition property in MetricReport + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/44512/24 + +- Add GET method for TriggerCollection + file://telemetry/0005-Add-GET-method-for-TriggerCollection.patch + +- LogService field, actual implementation will be upstreamed with triggers feature + file://telemetry/0006-Revert-Remove-LogService-from-TelemetryService.patch + +- Event service fix for Context field + file://telemetry/0007-event-service-fix-added-Context-field-to-response.patch + +- Generalize ReadingType in MetricDefinition + file://telemetry/0008-Generalize-ReadingType-in-MetricDefinition.patch + +- Add support for deleting terminated subscriptions + file://telemetry/0009-Add-support-for-deleting-terminated-subscriptions.patch diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0001-Revert-Disable-nbd-proxy-from-the-build.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0001-Revert-Disable-nbd-proxy-from-the-build.patch new file mode 100644 index 000000000..de316c4fa --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0001-Revert-Disable-nbd-proxy-from-the-build.patch @@ -0,0 +1,61 @@ +From b6863f9a0c1c36705eba0c3181541f67cd1a202a Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Wed, 14 Jul 2021 09:04:42 +0000 +Subject: [PATCH] Revert "Disable nbd proxy from the build" + +NBD Proxy has been disabled upstream. Reenable as we use it for Virtual +Media + +This reverts commit efb8062c306474942bc94f15d748b2eb0b58fbb6. + +Change-Id: I19a88b30c1074dd376f2df8f5668245b638b881f +--- + meson.build | 3 ++- + meson_options.txt | 10 ++-------- + 2 files changed, 4 insertions(+), 9 deletions(-) + +diff --git a/meson.build b/meson.build +index 650a5ec..5738b10 100644 +--- a/meson.build ++++ b/meson.build +@@ -83,7 +83,8 @@ feature_map = { + 'rest' : '-DBMCWEB_ENABLE_DBUS_REST', + 'static-hosting' : '-DBMCWEB_ENABLE_STATIC_HOSTING', + 'insecure-tftp-update' : '-DBMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE', +- #'vm-nbdproxy' : '-DBMCWEB_ENABLE_VM_NBDPROXY', ++ 'validate-unsecure-feature' : '-DBMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE', ++ 'vm-nbdproxy' : '-DBMCWEB_ENABLE_VM_NBDPROXY', + 'vm-websocket' : '-DBMCWEB_ENABLE_VM_WEBSOCKET', + } + +diff --git a/meson_options.txt b/meson_options.txt +index ff5b887..645f224 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -3,14 +3,7 @@ option('yocto-deps', type: 'feature', value: 'disabled', description : 'Use YOCT + option('kvm', type : 'feature',value : 'enabled', description : 'Enable the KVM host video WebSocket. Path is \'/kvm/0\'. Video is from the BMC\'s \'/dev/video\' device.') + option ('tests', type : 'feature', value : 'enabled', description : 'Enable Unit tests for bmcweb') + option('vm-websocket', type : 'feature', value : 'enabled', description : '''Enable the Virtual Media WebSocket. Path is \'/vm/0/0\'to open the websocket. See https://github.com/openbmc/jsnbd/blob/master/README.''') +- +-# if you use this option and are seeing this comment, please comment here: +-# https://github.com/openbmc/bmcweb/issues/188 and put forward your intentions +-# for this code. At this point, no daemon has been upstreamed that implements +-# this interface, so for the moment this appears to be dead code; In leiu of +-# removing it, it has been disabled to try to give those that use it the +-# opportunity to upstream their backend implementation +-#option('vm-nbdproxy', type: 'feature', value : 'disabled', description : 'Enable the Virtual Media WebSocket.') ++option('vm-nbdproxy', type: 'feature', value : 'disabled', description : 'Enable the Virtual Media WebSocket.') + option('rest', type : 'feature', value : 'enabled', description : '''Enable Phosphor REST (D-Bus) APIs. Paths directly map Phosphor D-Bus object paths, for example, \'/xyz/openbmc_project/logging/entry/enumerate\'. See https://github.com/openbmc/docs/blob/master/rest-api.md.''') + option('redfish', type : 'feature',value : 'enabled', description: 'Enable Redfish APIs. Paths are under \'/redfish/v1/\'. See https://github.com/openbmc/bmcweb/blob/master/DEVELOPING.md#redfish.') + option('host-serial-socket', type : 'feature', value : 'enabled', description : 'Enable host serial console WebSocket. Path is \'/console0\'. See https://github.com/openbmc/docs/blob/master/console.md.') +@@ -39,6 +32,7 @@ option ('https_port', type : 'integer', min : 1, max : 65535, value : 443, descr + # the implications of doing so.In general, enabling these options will cause security + # problems of varying degrees + ++option ('validate-unsecure-feature', type : 'feature', value : 'disabled', description : '''Enables unsecure features required by validation. Note: mustbe turned off for production images.''') + option ('insecure-disable-csrf', type : 'feature', value : 'disabled', description : 'Disable CSRF prevention checks.Should be set to false for production systems.') + option ('insecure-disable-ssl', type : 'feature', value : 'disabled', description : 'Disable SSL ports. Should be set to false for production systems.') + option ('insecure-disable-auth', type : 'feature', value : 'disabled', description : 'Disable authentication on all ports. Should be set to false for production systems') +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0002-bmcweb-handle-device-or-resource-busy-exception.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0002-bmcweb-handle-device-or-resource-busy-exception.patch new file mode 100644 index 000000000..e267dc60f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0002-bmcweb-handle-device-or-resource-busy-exception.patch @@ -0,0 +1,214 @@ +From bbb69d73ca8b74d34fa250813123de4274d5327b Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Fri, 23 Jul 2021 12:07:02 +0000 +Subject: [PATCH] bmcweb handle device or resource busy exception + +Use async_method_call_timed() for mount/unmount dbus oprations. +Long mount/unmount times are supported by VirtualMedia service, +this works because of settable timeout property, available for each block +device. +Default dbus calls will timeout when mount/unmount timeout is long enough. + +Get mount/unmount timeout property and use it for mount/unmount calls. +Add handling of device or resource busy exception (EBUSY) that +can be thrown by VirtualMedia service during Mount/Unmount dbus operations. + +Tested: Verified that after mounting non-existing HTTPS resource + in proxy mode, VirtualMedia recovers restoring ready state + and returns EBUSY during that transition. + Verfied that resources can be mounted/unmounted in both legacy + and proxy mode. +Signed-off-by: Karol Wachowski <karol.wachowski@intel.com> +Change-Id: Ica62c34db0cce24c4c6169fc661edfde49e948d0 +--- + redfish-core/lib/virtual_media.hpp | 142 +++++++++++++++++++++-------- + 1 file changed, 105 insertions(+), 37 deletions(-) + +diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp +index 6e69f20..7fcbf73 100644 +--- a/redfish-core/lib/virtual_media.hpp ++++ b/redfish-core/lib/virtual_media.hpp +@@ -26,6 +26,8 @@ + #include <boost/url/url_view.hpp> + #include <registries/privilege_registry.hpp> + ++#include <chrono> ++ + namespace redfish + { + /** +@@ -162,6 +164,26 @@ inline void + } + } + ++/** ++ * @brief parses Timeout property and converts to microseconds ++ */ ++static std::optional<uint64_t> ++ vmParseTimeoutProperty(const std::variant<int>& timeoutProperty) ++{ ++ const int* timeoutValue = std::get_if<int>(&timeoutProperty); ++ if (timeoutValue) ++ { ++ constexpr int timeoutMarginSeconds = 10; ++ return std::chrono::duration_cast<std::chrono::microseconds>( ++ std::chrono::seconds(*timeoutValue + timeoutMarginSeconds)) ++ .count(); ++ } ++ else ++ { ++ return std::nullopt; ++ } ++} ++ + /** + * @brief Fill template for Virtual Media Item. + */ +@@ -712,22 +734,57 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + } + + crow::connections::systemBus->async_method_call( +- [asyncResp, secretPipe](const boost::system::error_code ec, +- bool success) { ++ [asyncResp, service, name, imageUrl, rw, unixFd, ++ secretPipe](const boost::system::error_code ec, ++ const std::variant<int> timeoutProperty) { + if (ec) + { + BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; + messages::internalError(asyncResp->res); ++ return; + } +- else if (!success) ++ ++ auto timeout = vmParseTimeoutProperty(timeoutProperty); ++ if (timeout == std::nullopt) + { +- BMCWEB_LOG_ERROR << "Service responded with error"; +- messages::generalError(asyncResp->res); ++ BMCWEB_LOG_ERROR << "Timeout property is empty."; ++ messages::internalError(asyncResp->res); ++ return; + } ++ ++ crow::connections::systemBus->async_method_call_timed( ++ [asyncResp, secretPipe](const boost::system::error_code ec, ++ bool success) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; ++ if (ec == boost::system::errc::device_or_resource_busy) ++ { ++ messages::resourceInUse(asyncResp->res); ++ } ++ else if (ec == boost::system::errc::permission_denied) ++ { ++ messages::accessDenied(asyncResp->res, ++ "VirtualMedia.Insert"); ++ } ++ else ++ { ++ messages::internalError(asyncResp->res); ++ } ++ } ++ else if (!success) ++ { ++ BMCWEB_LOG_ERROR << "Service responded with error "; ++ messages::generalError(asyncResp->res); ++ } ++ }, ++ service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, ++ "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", *timeout, ++ imageUrl, rw, unixFd); + }, + service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, +- "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw, +- unixFd); ++ "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.VirtualMedia.MountPoint", "Timeout"); + } + + /** +@@ -739,38 +796,49 @@ inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& service, const std::string& name, + bool legacy) + { ++ const std::string vmMode = legacy ? "Legacy" : "Proxy"; ++ const std::string objectPath = ++ "/xyz/openbmc_project/VirtualMedia/" + vmMode + "/" + name; ++ const std::string ifaceName = "xyz.openbmc_project.VirtualMedia." + vmMode; + +- // Legacy mount requires parameter with image +- if (legacy) +- { +- crow::connections::systemBus->async_method_call( +- [asyncResp](const boost::system::error_code ec) { +- if (ec) +- { +- BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; +- +- messages::internalError(asyncResp->res); +- return; +- } +- }, +- service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, +- "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount"); +- } +- else // proxy +- { +- crow::connections::systemBus->async_method_call( +- [asyncResp](const boost::system::error_code ec) { +- if (ec) +- { +- BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, service, name, objectPath, ++ ifaceName](const boost::system::error_code ec, ++ const std::variant<int> timeoutProperty) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } + +- messages::internalError(asyncResp->res); +- return; +- } +- }, +- service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name, +- "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount"); +- } ++ auto timeout = vmParseTimeoutProperty(timeoutProperty); ++ if (timeout == std::nullopt) ++ { ++ BMCWEB_LOG_ERROR << "Timeout property is empty."; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ crow::connections::systemBus->async_method_call_timed( ++ [asyncResp](const boost::system::error_code ec) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; ++ if (ec == boost::system::errc::device_or_resource_busy) ++ { ++ messages::resourceInUse(asyncResp->res); ++ } ++ else ++ { ++ messages::internalError(asyncResp->res); ++ } ++ return; ++ } ++ }, ++ service, objectPath, ifaceName, "Unmount", *timeout); ++ }, ++ service, objectPath, "org.freedesktop.DBus.Properties", "Get", ++ "xyz.openbmc_project.VirtualMedia.MountPoint", "Timeout"); + } + + inline void requestNBDVirtualMediaRoutes(App& app) +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0003-Add-ConnectedVia-property-to-virtual-media-item-temp.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0003-Add-ConnectedVia-property-to-virtual-media-item-temp.patch new file mode 100644 index 000000000..c8af3a659 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0003-Add-ConnectedVia-property-to-virtual-media-item-temp.patch @@ -0,0 +1,28 @@ +From 1abf9a1d336eed835472fe933210d3be7ad5ba7a Mon Sep 17 00:00:00 2001 +From: Karol Wachowski <karol.wachowski@intel.com> +Date: Thu, 11 Feb 2021 08:35:41 +0000 +Subject: [PATCH] Add ConnectedVia property to virtual media item template + +Tested: Verified that ConnectedVia property is returned and set to + "NotConnected" for disconnected media. + +Signed-off-by: Karol Wachowski <karol.wachowski@intel.com> +--- + redfish-core/lib/virtual_media.hpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp +index 57c2bd2..de1cc94 100644 +--- a/redfish-core/lib/virtual_media.hpp ++++ b/redfish-core/lib/virtual_media.hpp +@@ -200,6 +200,7 @@ inline nlohmann::json vmItemTemplate(const std::string& name, + item["@odata.id"] = std::move(id); + + item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia"; ++ item["ConnectedVia"] = "NotConnected"; + item["Name"] = "Virtual Removable Media"; + item["Id"] = resName; + item["WriteProtected"] = true; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0004-Invalid-status-code-from-InsertMedia-REST-methods.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0004-Invalid-status-code-from-InsertMedia-REST-methods.patch new file mode 100644 index 000000000..439b05b3c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0004-Invalid-status-code-from-InsertMedia-REST-methods.patch @@ -0,0 +1,175 @@ +From 437a2a854303ed4e05344684b1990806464268cd Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Thu, 1 Jul 2021 10:08:27 +0000 +Subject: [PATCH] Invalid status code from InsertMedia REST methods GET, PUT, + DELETE, PATCH in proxy mode + +Add handlers for GET, PUT, DELETE, PATCH method and function that +checks which mode is used and set suitable status code: +Not allowed for Legacy and Not found for Proxy. + +Change-Id: Ib4c0a3e9a2a8853caa74c59239d9fcfed99c5e8b +Signed-off-by: Alicja Rybak <alicja.rybak@intel.com> +Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> +--- + redfish-core/lib/virtual_media.hpp | 137 +++++++++++++++++++++++++++++ + 1 file changed, 137 insertions(+) + +diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp +index 3b9f7ef..7d77b9f 100644 +--- a/redfish-core/lib/virtual_media.hpp ++++ b/redfish-core/lib/virtual_media.hpp +@@ -30,6 +30,117 @@ + + namespace redfish + { ++ ++/** ++ * @brief Function checks if insert media request is Legacy or Proxy type ++ * and sets suitable response code for unsupported REST method. ++ * ++ */ ++void CheckProxyMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, ++ const crow::Request& req, const std::string& name, ++ const std::string& resName) ++{ ++ if (name != "bmc") ++ { ++ messages::resourceNotFound(aResp->res, "VirtualMedia.Insert", resName); ++ ++ return; ++ } ++ ++ crow::connections::systemBus->async_method_call( ++ [aResp, req, resName](const boost::system::error_code ec, ++ const GetObjectType& getObjectType) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " ++ << ec; ++ aResp->res.result(boost::beast::http::status::not_found); ++ ++ return; ++ } ++ ++ if (getObjectType.size() == 0) ++ { ++ BMCWEB_LOG_ERROR << "ObjectMapper : No Service found"; ++ aResp->res.result(boost::beast::http::status::not_found); ++ return; ++ } ++ ++ std::string service = getObjectType.begin()->first; ++ BMCWEB_LOG_DEBUG << "GetObjectType: " << service; ++ ++ crow::connections::systemBus->async_method_call( ++ [service, resName, req, ++ aResp](const boost::system::error_code ec, ++ ManagedObjectType& subtree) { ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG << "DBUS response error"; ++ ++ return; ++ } ++ ++ for (auto& item : subtree) ++ { ++ std::string thispath = item.first.filename(); ++ if (thispath.empty()) ++ { ++ continue; ++ } ++ ++ if (thispath != resName) ++ { ++ continue; ++ } ++ ++ auto mode = item.first.parent_path(); ++ auto type = mode.parent_path(); ++ if (mode.filename().empty() || type.filename().empty()) ++ { ++ continue; ++ } ++ ++ if (type.filename() != "VirtualMedia") ++ { ++ continue; ++ } ++ ++ // Check if dbus path is Legacy type ++ if (mode.filename() == "Legacy") ++ { ++ BMCWEB_LOG_DEBUG << "InsertMedia only allowed " ++ "with POST method " ++ "in legacy mode"; ++ aResp->res.result( ++ boost::beast::http::status::method_not_allowed); ++ ++ return; ++ } ++ // Check if dbus path is Proxy type ++ if (mode.filename() == "Proxy") ++ { ++ // Not possible in proxy mode ++ BMCWEB_LOG_DEBUG << "InsertMedia not " ++ "allowed in proxy mode"; ++ aResp->res.result( ++ boost::beast::http::status::not_found); ++ ++ return; ++ } ++ } ++ ++ BMCWEB_LOG_DEBUG << "Parent item not found"; ++ aResp->res.result(boost::beast::http::status::not_found); ++ }, ++ service, "/xyz/openbmc_project/VirtualMedia", ++ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject", ++ "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>()); ++} ++ + /** + * @brief Function extracts transfer protocol name from URI. + */ +@@ -844,6 +955,32 @@ inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + + inline void requestNBDVirtualMediaRoutes(App& app) + { ++ BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/" ++ "VirtualMedia.InsertMedia") ++ .privileges({{"Login"}}) ++ .methods(boost::beast::http::verb::get)( ++ [](const crow::Request& req, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ const std::string& name, const std::string& resName) { ++ CheckProxyMode(asyncResp, req, name, resName); ++ }); ++ ++ for (auto method : ++ {boost::beast::http::verb::patch, boost::beast::http::verb::put, ++ boost::beast::http::verb::delete_}) ++ { ++ BMCWEB_ROUTE(app, ++ "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/" ++ "VirtualMedia.InsertMedia") ++ .privileges({{"ConfigureManager"}}) ++ .methods(method)( ++ [](const crow::Request& req, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ const std::string& name, const std::string& resName) { ++ CheckProxyMode(asyncResp, req, name, resName); ++ }); ++ } ++ + BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/" + "VirtualMedia.InsertMedia") + .privileges(redfish::privileges::postVirtualMedia) +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0005-Set-Inserted-redfish-property-for-not-inserted-resou.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0005-Set-Inserted-redfish-property-for-not-inserted-resou.patch new file mode 100644 index 000000000..3d80aeb20 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0005-Set-Inserted-redfish-property-for-not-inserted-resou.patch @@ -0,0 +1,43 @@ +From 89ca38dbc3433b3b734a20068e599809f3bd7b90 Mon Sep 17 00:00:00 2001 +From: Karol Wachowski <karol.wachowski@intel.com> +Date: Tue, 23 Feb 2021 15:53:16 +0000 +Subject: [PATCH] Set Inserted redfish property for not inserted resources + +Tested: Verified that Inserted property is returned and set to + "false" for not inserted media. +Signed-off-by: Karol Wachowski <karol.wachowski@intel.com> +--- + redfish-core/lib/virtual_media.hpp | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp +index 3e28164..4c475b7 100644 +--- a/redfish-core/lib/virtual_media.hpp ++++ b/redfish-core/lib/virtual_media.hpp +@@ -96,6 +96,7 @@ static void + BMCWEB_LOG_DEBUG << "Value Active not found"; + return; + } ++ aResp->res.jsonValue["Inserted"] = *activeValue; + + const std::string* endpointIdValue = + std::get_if<std::string>(&endpointIdProperty->second); +@@ -107,7 +108,6 @@ static void + aResp->res.jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] = + *endpointIdValue; + aResp->res.jsonValue["TransferProtocolType"] = "OEM"; +- aResp->res.jsonValue["Inserted"] = *activeValue; + if (*activeValue == true) + { + aResp->res.jsonValue["ConnectedVia"] = "Applet"; +@@ -138,7 +138,6 @@ static void + } + + aResp->res.jsonValue["Image"] = *imageUrlValue; +- aResp->res.jsonValue["Inserted"] = *activeValue; + aResp->res.jsonValue["TransferProtocolType"] = + getTransferProtocolTypeFromUri(*imageUrlValue); + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0006-Bmcweb-handle-permission-denied-exception.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0006-Bmcweb-handle-permission-denied-exception.patch new file mode 100644 index 000000000..6ad7cf174 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0006-Bmcweb-handle-permission-denied-exception.patch @@ -0,0 +1,37 @@ +From 1d69a22d0ec1eddbe6d703d6824f413f6b68399e Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Thu, 1 Jul 2021 10:41:47 +0000 +Subject: [PATCH] Bmcweb handle permission denied exception + +Add handling of permission denied exception (EPERM) that +can be thrown by VirtualMedia service during Mount/Unmount dbus operations. + +Tested: +Verified that after mounting/unmounting HTTPS resource twice in a row in legacy mode, +VirtualMedia returns EPERM, which bmcweb handles as 403 status code. + +Change-Id: Ibc18d5ec822c5072605b1fc4651389982002798b +Signed-off-by: Alicja Rybak <alicja.rybak@intel.com> +--- + redfish-core/lib/virtual_media.hpp | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp +index 186c04b..8e7c2e4 100644 +--- a/redfish-core/lib/virtual_media.hpp ++++ b/redfish-core/lib/virtual_media.hpp +@@ -917,6 +917,11 @@ inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + { + messages::resourceInUse(asyncResp->res); + } ++ else if (ec == boost::system::errc::permission_denied) ++ { ++ messages::accessDenied(asyncResp->res, ++ "VirtualMedia.Insert"); ++ } + else + { + messages::internalError(asyncResp->res); +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0007-Fix-unmounting-image-in-proxy-mode.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0007-Fix-unmounting-image-in-proxy-mode.patch new file mode 100644 index 000000000..88fa89465 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0007-Fix-unmounting-image-in-proxy-mode.patch @@ -0,0 +1,35 @@ +From 6f4b5fc1879f39b0f5fee0838f0ecbc481275d5e Mon Sep 17 00:00:00 2001 +From: Alicja Rybak <alicja.rybak@intel.com> +Date: Fri, 23 Apr 2021 17:35:52 +0200 +Subject: [PATCH] Fix unmounting image in proxy mode. + +Sometimes Slot0 got higher key than Slot1 and erase function for Slot1 +invalidates elements with keys not less than the erased element. +In that case invalid slot0 will be unmounted. +Change order of calling close() and erase() functions to +unmount correct device. + +Change-Id: I7a40a4518982f697d3eed635cde6d06978149cf0 +Signed-off-by: Alicja Rybak <alicja.rybak@intel.com> +--- + include/nbd_proxy.hpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/nbd_proxy.hpp b/include/nbd_proxy.hpp +index 3b28823..897bcf2 100644 +--- a/include/nbd_proxy.hpp ++++ b/include/nbd_proxy.hpp +@@ -439,9 +439,9 @@ inline void requestRoutes(App& app) + BMCWEB_LOG_DEBUG << "No session to close"; + return; + } ++ session->second->close(); + // Remove reference to session in global map + sessions.erase(session); +- session->second->close(); + }) + .onmessage([](crow::websocket::Connection& conn, + const std::string& data, bool) { +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend new file mode 100644 index 000000000..a716e612d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend @@ -0,0 +1,99 @@ +SRC_URI = "git://github.com/openbmc/bmcweb.git" +SRCREV = "b7ff344535c42af074c60bfb272ef66a2ba157b4" + +DEPENDS += "boost-url" + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +# add a user called bmcweb for the server to assume +# bmcweb is part of group shadow for non-root pam authentication +USERADD_PARAM:${PN} = "-r -s /usr/sbin/nologin -d /home/bmcweb -m -G shadow bmcweb" + +GROUPADD_PARAM:${PN} = "web; redfish " + +SRC_URI += "file://0001-Firmware-update-configuration-changes.patch \ + file://0002-Use-chip-id-based-UUID-for-Service-Root.patch \ + file://0010-managers-add-attributes-for-Manager.CommandShell.patch \ + file://0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch \ + file://0012-Log-RedFish-event-for-Invalid-login-attempt.patch \ + file://0013-Add-UART-routing-logic-into-host-console-connection-.patch \ + file://0014-recommended-fixes-by-crypto-review-team.patch \ + file://0015-Add-state-sensor-messages-to-the-registry.patch \ + file://0016-Fix-bmcweb-crashes-if-socket-directory-not-present.patch \ + file://0017-Add-msg-registry-for-subscription-related-actions.patch \ + file://0018-bmcweb-Add-BMC-Time-update-log-to-the-registry.patch \ + file://0019-Add-generic-message-PropertySizeExceeded.patch \ + file://0020-Redfish-Deny-set-AccountLockDuration-to-zero.patch \ + file://0023-Add-get-IPMI-session-id-s-to-Redfish.patch \ + file://0024-Add-count-sensor-type.patch \ + file://0025-Add-Model-CoreCount-to-ProcessorSummary.patch \ +" + +# OOB Bios Config: +SRC_URI += "file://biosconfig/0001-Define-Redfish-interface-Registries-Bios.patch \ + file://biosconfig/0002-BaseBiosTable-Add-support-for-PATCH-operation.patch \ + file://biosconfig/0003-Add-support-to-ResetBios-action.patch \ + file://biosconfig/0004-Add-support-to-ChangePassword-action.patch \ + file://biosconfig/0005-Fix-remove-bios-user-pwd-change-option-via-Redfish.patch \ + file://biosconfig/0006-Add-fix-for-broken-feature-Pending-Attributes.patch \ +" + +# Virtual Media: Backend code is not upstreamed so downstream only patches. +SRC_URI += "file://vm/0001-Revert-Disable-nbd-proxy-from-the-build.patch \ + file://vm/0002-bmcweb-handle-device-or-resource-busy-exception.patch \ + file://vm/0003-Add-ConnectedVia-property-to-virtual-media-item-temp.patch \ + file://vm/0004-Invalid-status-code-from-InsertMedia-REST-methods.patch \ + file://vm/0005-Set-Inserted-redfish-property-for-not-inserted-resou.patch \ + file://vm/0006-Bmcweb-handle-permission-denied-exception.patch \ + file://vm/0007-Fix-unmounting-image-in-proxy-mode.patch \ +" + +# EventService: Temporary pulled to downstream. See eventservice\README for details +SRC_URI += "file://eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch \ + file://eventservice/0002-EventService-https-client-support.patch \ + file://eventservice/0004-Add-Server-Sent-Events-support.patch \ + file://eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch \ + file://eventservice/0006-Add-EventService-SSE-filter-support.patch \ + file://eventservice/0007-EventService-Log-events-for-subscription-actions.patch \ + file://eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch \ + file://eventservice/0009-Restructure-Redifsh-EventLog-Transmit-code-flow.patch \ + file://eventservice/0010-Remove-Terminated-Event-Subscriptions.patch \ + file://eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch \ +" + +# Temporary downstream mirror of upstream patches, see telemetry\README for details +SRC_URI += " file://telemetry/0001-Add-support-for-MetricDefinition-scheme.patch \ + file://telemetry/0002-Sync-Telmetry-service-with-EventService.patch \ + file://telemetry/0003-Switched-bmcweb-to-use-new-telemetry-service-API.patch \ + file://telemetry/0004-Add-support-for-MetricDefinition-property-in-MetricReport.patch \ + file://telemetry/0005-Add-GET-method-for-TriggerCollection.patch \ + file://telemetry/0006-Revert-Remove-LogService-from-TelemetryService.patch \ + file://telemetry/0007-event-service-fix-added-Context-field-to-response.patch \ + file://telemetry/0009-Add-support-for-deleting-terminated-subscriptions.patch \ +" + +# Temporary downstream patch for routing and privilege changes +SRC_URI += " file://http_routing/0001-Add-asyncResp-support-during-handleUpgrade.patch \ + file://http_routing/0002-Move-privileges-to-separate-entity.patch \ + file://http_routing/0003-Add-Support-for-privilege-check-in-handleUpgrade.patch \ + file://http_routing/0004-Add-Privileges-to-Websockets.patch \ + file://http_routing/0005-Add-Privileges-to-SseSockets.patch \ +" + +# Temporary fix: Move it to service file +do_install:append() { + install -d ${D}/var/lib/bmcweb + install -d ${D}/etc/ssl/certs/authority +} + +# Enable PFR support +EXTRA_OEMESON += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', '-Dredfish-provisioning-feature=enabled', '', d)}" + +# Enable NBD proxy embedded in bmcweb +EXTRA_OEMESON += " -Dvm-nbdproxy=enabled" + +# Disable dependency on external nbd-proxy application +EXTRA_OEMESON += " -Dvm-websocket=disabled" +RDEPENDS:${PN}:remove += "jsnbd" + + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend new file mode 100644 index 000000000..73c3f2190 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend @@ -0,0 +1,21 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" +SRC_URI += " file://dev_id.json \ + file://channel_access.json \ + file://channel_config.json \ + file://master_write_read_white_list.json \ + " + +FILES:${PN} += " \ + ${datadir}/ipmi-providers/channel_access.json \ + ${datadir}/ipmi-providers/channel_config.json \ + ${datadir}/ipmi-providers/master_write_read_white_list.json \ + " + +do_install:append() { + install -m 0644 -D ${WORKDIR}/channel_access.json \ + ${D}${datadir}/ipmi-providers/channel_access.json + install -m 0644 -D ${WORKDIR}/channel_config.json \ + ${D}${datadir}/ipmi-providers/channel_config.json + install -m 0644 -D ${WORKDIR}/master_write_read_white_list.json \ + ${D}${datadir}/ipmi-providers/master_write_read_white_list.json +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_access.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_access.json new file mode 100644 index 000000000..299483121 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_access.json @@ -0,0 +1,23 @@ +{ + "1" : { + "access_mode" : "always_available", + "user_auth_disabled" : false, + "per_msg_auth_disabled" : false, + "alerting_disabled" : false, + "priv_limit" : "priv-admin" + }, + "2" : { + "access_mode" : "always_available", + "user_auth_disabled" : false, + "per_msg_auth_disabled" : false, + "alerting_disabled" : false, + "priv_limit" : "priv-admin" + }, + "3" : { + "access_mode" : "always_available", + "user_auth_disabled" : false, + "per_msg_auth_disabled" : false, + "alerting_disabled" : false, + "priv_limit" : "priv-admin" + } +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json new file mode 100644 index 000000000..73fb214a2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json @@ -0,0 +1,179 @@ +{ + "0" : { + "name" : "Ipmb", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "1" : { + "name" : "eth1", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "lan-802.3", + "protocol_type" : "ipmb-1.0", + "session_supported" : "multi-session", + "is_ipmi" : true + } + }, + "2" : { + "name" : "RESERVED", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "reserved", + "protocol_type" : "na", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "3" : { + "name" : "eth0", + "is_valid" : true, + "active_sessions" : 0, + "is_management_nic" : true, + "channel_info" : { + "medium_type" : "lan-802.3", + "protocol_type" : "ipmb-1.0", + "session_supported" : "multi-session", + "is_ipmi" : true + } + }, + "4" : { + "name" : "RESERVED", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "reserved", + "protocol_type" : "na", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "5" : { + "name" : "RESERVED", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "reserved", + "protocol_type" : "na", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "6" : { + "name" : "SMLINK", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "7" : { + "name" : "ipmi_kcs4", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "system-interface", + "protocol_type" : "kcs", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "8" : { + "name" : "INTRABMC", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "oem", + "protocol_type" : "oem", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "9" : { + "name" : "SIPMB", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "10" : { + "name" : "PCIE", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "11" : { + "name" : "RESERVED", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "reserved", + "protocol_type" : "na", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "12" : { + "name" : "INTERNAL", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "13" : { + "name" : "RESERVED", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "reserved", + "protocol_type" : "na", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "14" : { + "name" : "SELF", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "unknown", + "protocol_type" : "na", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "15" : { + "name" : "ipmi_kcs3", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "system-interface", + "protocol_type" : "kcs", + "session_supported" : "session-less", + "is_ipmi" : true + } + } +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/dev_id.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/dev_id.json new file mode 100644 index 000000000..e561569d9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/dev_id.json @@ -0,0 +1,2 @@ +{"id": 35, "revision": 0, "addn_dev_support": 191, + "manuf_id": 343, "prod_id": 123, "aux": 0} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/master_write_read_white_list.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/master_write_read_white_list.json new file mode 100644 index 000000000..6fc46f452 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/master_write_read_white_list.json @@ -0,0 +1,76 @@ +{ + "filters": [ + { + "Description": "Allow full read - ignore first byte write value", + "busId": "0x01", + "slaveAddr": "0x4d", + "slaveAddrMask": "0x00", + "command": "0x00", + "commandMask": "0xFF" + }, + { + "Description": "Allow full read - ignore first byte write value", + "busId": "0x01", + "slaveAddr": "0x57", + "slaveAddrMask": "0x00", + "command": "0x00", + "commandMask": "0xFF" + }, + { + "Description": "Allow full read - ignore first byte write value", + "busId": "0x02", + "slaveAddr": "0x40", + "slaveAddrMask": "0x00", + "command": "0x00", + "commandMask": "0xFF" + }, + { + "Description": "Allow full read - ignore first byte write value", + "busId": "0x02", + "slaveAddr": "0x49", + "slaveAddrMask": "0x00", + "command": "0x00", + "commandMask": "0xFF" + }, + { + "Description": "Allow full read - ignore first byte write value", + "busId": "0x02", + "slaveAddr": "0x51", + "slaveAddrMask": "0x00", + "command": "0x00", + "commandMask": "0xFF" + }, + { + "Description": "Allow full read - ignore first byte write value", + "busId": "0x03", + "slaveAddr": "0x44", + "slaveAddrMask": "0x00", + "command": "0x00", + "commandMask": "0xFF" + }, + { + "Description": "Allow full read - ignore first byte write value", + "busId": "0x03", + "slaveAddr": "0x68", + "slaveAddrMask": "0x00", + "command": "0x00", + "commandMask": "0xFF" + }, + { + "Description": "Allow full read - ignore first byte write value", + "busId": "0x06", + "slaveAddr": "0x40", + "slaveAddrMask": "0x00", + "command": "0x00", + "commandMask": "0xFF" + }, + { + "Description": "Allow full read - ignore first byte write value", + "busId": "0x07", + "slaveAddr": "0x51", + "slaveAddrMask": "0x00", + "command": "0x00", + "commandMask": "0xFF" + } + ] +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native.bbappend new file mode 100644 index 000000000..3a2cf0e01 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native.bbappend @@ -0,0 +1,8 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" +SRC_URI += " file://config.yaml" + +#override source file before it is used for final FRU file (merged from multiple sources) +do_install() { + cp ${WORKDIR}/config.yaml ${config_datadir}/ +} + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native/config.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native/config.yaml new file mode 100644 index 000000000..e9b7a621e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native/config.yaml @@ -0,0 +1,31 @@ +# A YAML similar to this example would have to be generated, for eg with MRW +# inputs and system configuration, to depict IPMI Fru information. +# +# This file maps IPMI properties to phosphor dbus inventory properties +# +# This YAML could help generate C++ code. +# Format of the YAML: +# Fruid: +# Associated Fru paths +# d-bus Interfaces +# d-bus Properties +# IPMI Fru mapping +0: + /system/board/WFP_Baseboard: + entityID: 23 + entityInstance: 1 + interfaces: + xyz.openbmc_project.Inventory.Item: + name: + IPMIFruProperty: Product Name + IPMIFruSection: Product + xyz.openbmc_project.Inventory.Decorator.Asset: + Manufacturer: + IPMIFruProperty: Manufacturer + IPMIFruSection: Product + PartNumber: + IPMIFruProperty: Part Number + IPMIFruSection: Product + SerialNumber: + IPMIFruProperty: Serial Number + IPMIFruSection: Product diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/.clang-format b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/.clang-format new file mode 100644 index 000000000..ea71ad6e1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/.clang-format @@ -0,0 +1,99 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +PointerAlignment: Left +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^[<"](gtest|gmock)' + Priority: 5 + - Regex: '^"config.h"' + Priority: -1 + - Regex: '^".*\.hpp"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0010-fix-get-system-GUID-ipmi-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0010-fix-get-system-GUID-ipmi-command.patch new file mode 100644 index 000000000..a7d09f61e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0010-fix-get-system-GUID-ipmi-command.patch @@ -0,0 +1,41 @@ +From f18efe239cb4bbfd6996f753ae694f81041d8d43 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Fri, 14 Feb 2020 13:13:06 -0800 +Subject: [PATCH] Fix 'Get System GUID' to use settings UUID + +The upstream Get System GUID command looks first for a BMC interface +and then assumes that the UUID interface is next to that. But that is +not the case on Intel systems where the system GUID is found in the +settings daemon. + +Change-Id: I924bd05e0a546f2b30288c1faf72157296ab6579 +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + apphandler.cpp | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index 90818a9..dcf2c86 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -788,8 +788,6 @@ auto ipmiAppGetBtCapabilities() + + auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>> + { +- static constexpr auto bmcInterface = +- "xyz.openbmc_project.Inventory.Item.Bmc"; + static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID"; + static constexpr auto uuidProperty = "UUID"; + +@@ -798,7 +796,7 @@ auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>> + { + // Get the Inventory object implementing BMC interface + auto busPtr = getSdBus(); +- auto objectInfo = ipmi::getDbusObject(*busPtr, bmcInterface); ++ auto objectInfo = ipmi::getDbusObject(*busPtr, uuidInterface); + + // Read UUID property value from bmcObject + // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0053-Fix-keep-looping-issue-when-entering-OS.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0053-Fix-keep-looping-issue-when-entering-OS.patch new file mode 100644 index 000000000..0d86a4223 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0053-Fix-keep-looping-issue-when-entering-OS.patch @@ -0,0 +1,89 @@ +From 58771a22dfcaa1e67bcf4fc0bd2ce0aa28c67e3f Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@linux.intel.com> +Date: Wed, 23 Jan 2019 17:02:40 +0800 +Subject: [PATCH] Fix keep looping issue when entering OS + +Sometimes when entering OS, OS will keep continuously sending ipmi command +"READ EVENT MESSAGE BUFFER" to BMC. This issue is caused by incorrect KCS +status. If restart the host immediately while OS is still running, SMS_ATN +will be set, after that KCS come into an incorrect status, and then KCS +communction between BMC and OS crash. To make KCS go back to correct status +and fix the issue, clear SMS_ATN after every time power cycle happen. + +Unit Test: + After entered OS, force reset system, after enter OS again, OS can start +normally without keep sending READ EVENT MESSAGE BUFFER command. + After power on system, enter EFI SHELL, check IPMI can work +correctly through KCS channel. +--- + host-cmd-manager.cpp | 33 ++++++++++++++++++++++++--------- + 1 file changed, 24 insertions(+), 9 deletions(-) + +diff --git a/host-cmd-manager.cpp b/host-cmd-manager.cpp +index e52c9bb..b3a5d71 100644 +--- a/host-cmd-manager.cpp ++++ b/host-cmd-manager.cpp +@@ -23,6 +23,8 @@ namespace command + constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0"; + constexpr auto HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host"; + constexpr auto HOST_TRANS_PROP = "RequestedHostTransition"; ++constexpr const char* IPMI_PATH = "/xyz/openbmc_project/Ipmi/Channel/ipmi_kcs3"; ++constexpr const char* IPMI_INTERFACE = "xyz.openbmc_project.Ipmi.Channel.SMS"; + + // For throwing exceptions + using namespace phosphor::logging; +@@ -103,6 +105,20 @@ void Manager::clearQueue() + // `false` indicating Failure + std::get<CallBack>(command)(ipmiCmdData, false); + } ++ ++ auto host = ::ipmi::getService(this->bus, IPMI_INTERFACE, IPMI_PATH); ++ auto method = this->bus.new_method_call(host.c_str(), IPMI_PATH, ++ IPMI_INTERFACE, "clearAttention"); ++ ++ try ++ { ++ auto reply = this->bus.call(method); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ log<level::ERR>("Error in clearing SMS attention"); ++ elog<InternalFailure>(); ++ } + } + + // Called for alerting the host +@@ -112,9 +128,7 @@ void Manager::checkQueueAndAlertHost() + { + log<level::DEBUG>("Asserting SMS Attention"); + +- std::string HOST_IPMI_SVC("org.openbmc.HostIpmi"); +- std::string IPMI_PATH("/org/openbmc/HostIpmi/1"); +- std::string IPMI_INTERFACE("org.openbmc.HostIpmi"); ++ auto host = ::ipmi::getService(this->bus, IPMI_INTERFACE, IPMI_PATH); + + // Start the timer for this transaction + auto time = std::chrono::duration_cast<std::chrono::microseconds>( +@@ -127,12 +141,13 @@ void Manager::checkQueueAndAlertHost() + return; + } + +- auto method = +- this->bus.new_method_call(HOST_IPMI_SVC.c_str(), IPMI_PATH.c_str(), +- IPMI_INTERFACE.c_str(), "setAttention"); +- auto reply = this->bus.call(method); +- +- if (reply.is_method_error()) ++ auto method = this->bus.new_method_call(host.c_str(), IPMI_PATH, ++ IPMI_INTERFACE, "setAttention"); ++ try ++ { ++ auto reply = this->bus.call(method); ++ } ++ catch (const std::exception&) + { + log<level::ERR>("Error in setting SMS attention"); + elog<InternalFailure>(); +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0059-Move-Set-SOL-config-parameter-to-host-ipmid.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0059-Move-Set-SOL-config-parameter-to-host-ipmid.patch new file mode 100644 index 000000000..ba8896195 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0059-Move-Set-SOL-config-parameter-to-host-ipmid.patch @@ -0,0 +1,356 @@ +From 9f4fb8a6aa076261b19c187aeef840d818158ec7 Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@intel.com> +Date: Wed, 16 Oct 2019 14:24:20 +0800 +Subject: [PATCH] Move Set SOL config parameter to host-ipmid + +Move Set SOL config parameter command from net-ipmid to host-ipmid, +so that BIOS in Intel platform can enable or disable SOL through KCS. +Get SOL config parameter command will be moved later. + +Tested by: +With the related change in phospher-ipmi-net and phospher-dbus-interface, +Run commands: +ipmitool raw 0x0c 0x21 0x0e 0x00 0x01 +ipmitool raw 0x0c 0x21 0x0e 0x01 0x00 +ipmitool raw 0x0c 0x21 0x0e 0x02 0x03 +ipmitool raw 0x0c 0x21 0x0e 0x03 0x5 0x03 +ipmitool raw 0x0c 0x21 0x0e 0x04 0x5 0x03 +All these commands have correct response and all dbus interface for +sol command change to same value in above commands. +After reboot BMC, "Progress" property in dbus interface change back +to 0 and other properties will not reset to default value. + +Signed-off-by: Cheng C Yang <cheng.c.yang@intel.com> +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + host-ipmid-whitelist.conf | 1 + + transporthandler.cpp | 294 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 295 insertions(+) + +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index 5397115..c93f3b1 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -41,6 +41,7 @@ + 0x0A:0x48 //<Storage>:<Get SEL Time> + 0x0A:0x49 //<Storage>:<Set SEL Time> + 0x0C:0x02 //<Transport>:<Get LAN Configuration Parameters> ++0x0C:0x21 //<Transport>:<Set SOL Configuration Parameters> + 0x2C:0x00 //<Group Extension>:<Group Extension Command> + 0x2C:0x01 //<Group Extension>:<Get DCMI Capabilities> + 0x2C:0x02 //<Group Extension>:<Get Power Reading> +diff --git a/transporthandler.cpp b/transporthandler.cpp +index 0012746..0de76c4 100644 +--- a/transporthandler.cpp ++++ b/transporthandler.cpp +@@ -2030,8 +2030,298 @@ RspType<message::Payload> getLan(Context::ptr ctx, uint4_t channelBits, + } // namespace transport + } // namespace ipmi + ++constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL"; ++constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/"; ++ + void register_netfn_transport_functions() __attribute__((constructor)); + ++static std::string ++ getSOLService(std::shared_ptr<sdbusplus::asio::connection> dbus, ++ const std::string& solPathWitheEthName) ++{ ++ static std::string solService{}; ++ if (solService.empty()) ++ { ++ try ++ { ++ solService = ++ ipmi::getService(*dbus, solInterface, solPathWitheEthName); ++ } ++ catch (const sdbusplus::exception::SdBusError& e) ++ { ++ solService.clear(); ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error: get SOL service failed"); ++ return solService; ++ } ++ } ++ return solService; ++} ++ ++static int setSOLParameter(const std::string& property, ++ const ipmi::Value& value, const uint8_t& channelNum) ++{ ++ auto dbus = getSdBus(); ++ ++ std::string ethdevice = ipmi::getChannelName(channelNum); ++ ++ std::string solPathWitheEthName = std::string(solPath) + ethdevice; ++ ++ std::string service = getSOLService(dbus, solPathWitheEthName); ++ if (service.empty()) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get SOL service failed"); ++ return -1; ++ } ++ try ++ { ++ ipmi::setDbusProperty(*dbus, service, solPathWitheEthName, solInterface, ++ property, value); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error setting sol parameter"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int getSOLParameter(const std::string& property, ipmi::Value& value, ++ const uint8_t& channelNum) ++{ ++ auto dbus = getSdBus(); ++ ++ std::string ethdevice = ipmi::getChannelName(channelNum); ++ ++ std::string solPathWitheEthName = std::string(solPath) + ethdevice; ++ ++ std::string service = getSOLService(dbus, solPathWitheEthName); ++ if (service.empty()) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get SOL service failed"); ++ return -1; ++ } ++ try ++ { ++ value = ipmi::getDbusProperty(*dbus, service, solPathWitheEthName, ++ solInterface, property); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error getting sol parameter"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static const constexpr uint8_t encryptMask = 0x80; ++static const constexpr uint8_t encryptShift = 7; ++static const constexpr uint8_t authMask = 0x40; ++static const constexpr uint8_t authShift = 6; ++static const constexpr uint8_t privilegeMask = 0xf; ++ ++namespace ipmi ++{ ++constexpr Cc ccParmNotSupported = 0x80; ++constexpr Cc ccSetInProgressActive = 0x81; ++constexpr Cc ccSystemInfoParameterSetReadOnly = 0x82; ++ ++static inline auto responseParmNotSupported() ++{ ++ return response(ccParmNotSupported); ++} ++static inline auto responseSetInProgressActive() ++{ ++ return response(ccSetInProgressActive); ++} ++static inline auto responseSystemInfoParameterSetReadOnly() ++{ ++ return response(ccSystemInfoParameterSetReadOnly); ++} ++ ++} // namespace ipmi ++ ++namespace sol ++{ ++enum class Parameter ++{ ++ progress, //!< Set In Progress. ++ enable, //!< SOL Enable. ++ authentication, //!< SOL Authentication. ++ accumulate, //!< Character Accumulate Interval & Send Threshold. ++ retry, //!< SOL Retry. ++ nvbitrate, //!< SOL non-volatile bit rate. ++ vbitrate, //!< SOL volatile bit rate. ++ channel, //!< SOL payload channel. ++ port, //!< SOL payload port. ++}; ++ ++enum class Privilege : uint8_t ++{ ++ highestPriv, ++ callbackPriv, ++ userPriv, ++ operatorPriv, ++ adminPriv, ++ oemPriv, ++}; ++ ++} // namespace sol ++ ++constexpr uint8_t progressMask = 0x03; ++constexpr uint8_t enableMask = 0x01; ++constexpr uint8_t retryMask = 0x07; ++ ++ipmi::RspType<> setSOLConfParams(ipmi::Context::ptr ctx, uint4_t chNum, ++ uint4_t reserved, uint8_t paramSelector, ++ uint8_t configParamData1, ++ std::optional<uint8_t> configParamData2) ++{ ++ ipmi::ChannelInfo chInfo; ++ uint8_t channelNum = ipmi::convertCurrentChannelNum( ++ static_cast<uint8_t>(chNum), ctx->channel); ++ if (reserved != 0 || ++ (!ipmi::isValidChannel(static_cast<uint8_t>(channelNum)))) ++ { ++ return ipmi::responseInvalidFieldRequest(); ++ } ++ ++ ipmi_ret_t compCode = ++ ipmi::getChannelInfo(static_cast<uint8_t>(channelNum), chInfo); ++ if (compCode != IPMI_CC_OK || ++ chInfo.mediumType != ++ static_cast<uint8_t>(ipmi::EChannelMediumType::lan8032)) ++ { ++ return ipmi::responseInvalidFieldRequest(); ++ } ++ ++ switch (static_cast<sol::Parameter>(paramSelector)) ++ { ++ case sol::Parameter::progress: ++ { ++ if (configParamData2) ++ { ++ return ipmi::responseReqDataLenInvalid(); ++ } ++ uint8_t progress = configParamData1 & progressMask; ++ ipmi::Value currentProgress = 0; ++ if (getSOLParameter("Progress", currentProgress, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ ++ if ((std::get<uint8_t>(currentProgress) == 1) && (progress == 1)) ++ { ++ return ipmi::responseSetInProgressActive(); ++ } ++ ++ if (setSOLParameter("Progress", progress, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ break; ++ } ++ case sol::Parameter::enable: ++ { ++ if (configParamData2) ++ { ++ return ipmi::responseReqDataLenInvalid(); ++ } ++ bool enable = configParamData1 & enableMask; ++ if (setSOLParameter("Enable", enable, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ break; ++ } ++ case sol::Parameter::authentication: ++ { ++ if (configParamData2) ++ { ++ return ipmi::responseReqDataLenInvalid(); ++ } ++ uint8_t encrypt = (configParamData1 & encryptMask) >> encryptShift; ++ uint8_t auth = (configParamData1 & authMask) >> authShift; ++ uint8_t privilege = configParamData1 & privilegeMask; ++ // For security considering encryption and authentication must be ++ // true. ++ if (!encrypt || !auth) ++ { ++ return ipmi::responseSystemInfoParameterSetReadOnly(); ++ } ++ else if (privilege < ++ static_cast<uint8_t>(sol::Privilege::userPriv) || ++ privilege > static_cast<uint8_t>(sol::Privilege::oemPriv)) ++ { ++ return ipmi::responseInvalidFieldRequest(); ++ } ++ ++ if (setSOLParameter("Privilege", privilege, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ ++ break; ++ } ++ case sol::Parameter::accumulate: ++ { ++ if (!configParamData2) ++ { ++ return ipmi::responseReqDataLenInvalid(); ++ } ++ if (*configParamData2 == 0) ++ { ++ return ipmi::responseInvalidFieldRequest(); ++ } ++ if (setSOLParameter("AccumulateIntervalMS", configParamData1, ++ channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ if (setSOLParameter("Threshold", *configParamData2, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ break; ++ } ++ case sol::Parameter::retry: ++ { ++ if (!configParamData2) ++ { ++ return ipmi::responseReqDataLenInvalid(); ++ } ++ if ((setSOLParameter( ++ "RetryCount", ++ static_cast<uint8_t>(configParamData1 & retryMask), ++ channelNum) < 0) || ++ (setSOLParameter("RetryIntervalMS", *configParamData2, ++ channelNum) < 0)) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ ++ break; ++ } ++ case sol::Parameter::port: ++ { ++ return ipmi::responseSystemInfoParameterSetReadOnly(); ++ } ++ case sol::Parameter::nvbitrate: ++ case sol::Parameter::vbitrate: ++ case sol::Parameter::channel: ++ default: ++ return ipmi::responseParmNotSupported(); ++ } ++ ++ return ipmi::responseSuccess(); ++} ++ + void register_netfn_transport_functions() + { + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, +@@ -2040,4 +2330,8 @@ void register_netfn_transport_functions() + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, + ipmi::transport::cmdGetLanConfigParameters, + ipmi::Privilege::Operator, ipmi::transport::getLan); ++ ++ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, ++ ipmi::transport::cmdSetSolConfigParameters, ++ ipmi::Privilege::Admin, setSOLConfParams); + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0060-Move-Get-SOL-config-parameter-to-host-ipmid.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0060-Move-Get-SOL-config-parameter-to-host-ipmid.patch new file mode 100644 index 000000000..d6ba70562 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0060-Move-Get-SOL-config-parameter-to-host-ipmid.patch @@ -0,0 +1,259 @@ +From ff1d4198e8ad8f824f34fb9d261ea0e25179f070 Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@intel.com> +Date: Thu, 11 Jul 2019 00:32:58 +0800 +Subject: [PATCH] Move Get SOL config parameter to host-ipmid + +Move Get SOL config parameter command from net-ipmid to host-ipmid. + +Tested: +Run command ipmitool sol info 1 +Set in progress : set-complete +Enabled : true +Force Encryption : false +Force Authentication : false +Privilege Level : ADMINISTRATOR +Character Accumulate Level (ms) : 60 +Character Send Threshold : 96 +Retry Count : 6 +Retry Interval (ms) : 200 +Volatile Bit Rate (kbps) : IPMI-Over-Serial-Setting +Non-Volatile Bit Rate (kbps) : 115.2 +Payload Channel : 1 (0x01) +Payload Port : 623 + +Signed-off-by: Cheng C Yang <cheng.c.yang@intel.com> +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + host-ipmid-whitelist.conf | 1 + + transporthandler.cpp | 191 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 192 insertions(+) + +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index c93f3b1..730437d 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -42,6 +42,7 @@ + 0x0A:0x49 //<Storage>:<Set SEL Time> + 0x0C:0x02 //<Transport>:<Get LAN Configuration Parameters> + 0x0C:0x21 //<Transport>:<Set SOL Configuration Parameters> ++0x0C:0x22 //<Transport>:<Get SOL Configuration Parameters> + 0x2C:0x00 //<Group Extension>:<Group Extension Command> + 0x2C:0x01 //<Group Extension>:<Get DCMI Capabilities> + 0x2C:0x02 //<Group Extension>:<Get Power Reading> +diff --git a/transporthandler.cpp b/transporthandler.cpp +index 0de76c4..b81e0d5 100644 +--- a/transporthandler.cpp ++++ b/transporthandler.cpp +@@ -2120,6 +2120,28 @@ static int getSOLParameter(const std::string& property, ipmi::Value& value, + return 0; + } + ++constexpr const char* consoleInterface = "xyz.openbmc_project.console"; ++constexpr const char* consolePath = "/xyz/openbmc_project/console"; ++static int getSOLBaudRate(ipmi::Value& value) ++{ ++ auto dbus = getSdBus(); ++ ++ try ++ { ++ value = ++ ipmi::getDbusProperty(*dbus, "xyz.openbmc_project.console", ++ consolePath, consoleInterface, "baudrate"); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error getting sol baud rate"); ++ return -1; ++ } ++ ++ return 0; ++} ++ + static const constexpr uint8_t encryptMask = 0x80; + static const constexpr uint8_t encryptShift = 7; + static const constexpr uint8_t authMask = 0x40; +@@ -2322,6 +2344,171 @@ ipmi::RspType<> setSOLConfParams(ipmi::Context::ptr ctx, uint4_t chNum, + return ipmi::responseSuccess(); + } + ++static const constexpr uint8_t retryCountMask = 0x07; ++static constexpr uint16_t ipmiStdPort = 623; ++static constexpr uint8_t solParameterRevision = 0x11; ++ipmi::RspType<uint8_t, std::optional<uint8_t>, std::optional<uint8_t>> ++ getSOLConfParams(ipmi::Context::ptr ctx, uint4_t chNum, uint3_t reserved, ++ bool getParamRev, uint8_t paramSelector, ++ uint8_t setSelector, uint8_t blockSelector) ++{ ++ ipmi::ChannelInfo chInfo; ++ uint8_t channelNum = ipmi::convertCurrentChannelNum( ++ static_cast<uint8_t>(chNum), ctx->channel); ++ if (reserved != 0 || ++ (!ipmi::isValidChannel(static_cast<uint8_t>(channelNum))) || ++ (ipmi::EChannelSessSupported::none == ++ ipmi::getChannelSessionSupport(static_cast<uint8_t>(channelNum)))) ++ { ++ return ipmi::responseInvalidFieldRequest(); ++ } ++ ipmi_ret_t compCode = ++ ipmi::getChannelInfo(static_cast<uint8_t>(channelNum), chInfo); ++ if (compCode != IPMI_CC_OK || ++ chInfo.mediumType != ++ static_cast<uint8_t>(ipmi::EChannelMediumType::lan8032)) ++ { ++ return ipmi::responseInvalidFieldRequest(); ++ } ++ ++ if (getParamRev) ++ { ++ return ipmi::responseSuccess(solParameterRevision, std::nullopt, ++ std::nullopt); ++ } ++ ++ ipmi::Value value; ++ switch (static_cast<sol::Parameter>(paramSelector)) ++ { ++ case sol::Parameter::progress: ++ { ++ if (getSOLParameter("Progress", value, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ return ipmi::responseSuccess( ++ solParameterRevision, std::get<uint8_t>(value), std::nullopt); ++ } ++ case sol::Parameter::enable: ++ { ++ if (getSOLParameter("Enable", value, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ return ipmi::responseSuccess( ++ solParameterRevision, ++ static_cast<uint8_t>(std::get<bool>(value)), std::nullopt); ++ } ++ case sol::Parameter::authentication: ++ { ++ uint8_t authentication = 0; ++ if (getSOLParameter("Privilege", value, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ authentication = (std::get<uint8_t>(value) & 0x0f); ++ ++ if (getSOLParameter("ForceAuthentication", value, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ authentication |= ++ (static_cast<uint8_t>(std::get<bool>(value)) << 6); ++ ++ if (getSOLParameter("ForceEncryption", value, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ authentication |= ++ (static_cast<uint8_t>(std::get<bool>(value)) << 7); ++ return ipmi::responseSuccess(solParameterRevision, authentication, ++ std::nullopt); ++ } ++ case sol::Parameter::accumulate: ++ { ++ if (getSOLParameter("AccumulateIntervalMS", value, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ ++ ipmi::Value value1; ++ if (getSOLParameter("Threshold", value1, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ return ipmi::responseSuccess(solParameterRevision, ++ std::get<uint8_t>(value), ++ std::get<uint8_t>(value1)); ++ } ++ case sol::Parameter::retry: ++ { ++ if (getSOLParameter("RetryCount", value, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ ++ ipmi::Value value1; ++ if (getSOLParameter("RetryIntervalMS", value1, channelNum) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ return ipmi::responseSuccess( ++ solParameterRevision, std::get<uint8_t>(value) & retryCountMask, ++ std::get<uint8_t>(value1)); ++ } ++ case sol::Parameter::channel: ++ { ++ return ipmi::responseSuccess(solParameterRevision, channelNum, ++ std::nullopt); ++ } ++ case sol::Parameter::port: ++ { ++ uint16_t port = htole16(ipmiStdPort); ++ auto buffer = reinterpret_cast<const uint8_t*>(&port); ++ return ipmi::responseSuccess(solParameterRevision, buffer[0], ++ buffer[1]); ++ } ++ case sol::Parameter::nvbitrate: ++ { ++ if (getSOLBaudRate(value) < 0) ++ { ++ return ipmi::responseUnspecifiedError(); ++ } ++ uint8_t bitRate = 0; ++ uint32_t* pBaudRate = std::get_if<uint32_t>(&value); ++ if (!pBaudRate) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Failed to get valid baud rate from D-Bus interface"); ++ } ++ switch (*pBaudRate) ++ { ++ case 9600: ++ bitRate = 0x06; ++ break; ++ case 19200: ++ bitRate = 0x07; ++ break; ++ case 38400: ++ bitRate = 0x08; ++ break; ++ case 57600: ++ bitRate = 0x09; ++ break; ++ case 115200: ++ bitRate = 0x0a; ++ break; ++ default: ++ break; ++ } ++ return ipmi::responseSuccess(solParameterRevision, bitRate, ++ std::nullopt); ++ } ++ default: ++ return ipmi::responseParmNotSupported(); ++ } ++} ++ + void register_netfn_transport_functions() + { + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, +@@ -2334,4 +2521,8 @@ void register_netfn_transport_functions() + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, + ipmi::transport::cmdSetSolConfigParameters, + ipmi::Privilege::Admin, setSOLConfParams); ++ ++ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, ++ ipmi::transport::cmdGetSolConfigParameters, ++ ipmi::Privilege::User, getSOLConfParams); + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0062-Update-IPMI-Chassis-Control-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0062-Update-IPMI-Chassis-Control-command.patch new file mode 100644 index 000000000..112c1ffab --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0062-Update-IPMI-Chassis-Control-command.patch @@ -0,0 +1,292 @@ +From 99f63d2af9f45badaa8aff4ef958443bea62ede8 Mon Sep 17 00:00:00 2001 +From: "Jason M. Bills" <jason.m.bills@linux.intel.com> +Date: Mon, 3 Jun 2019 17:01:47 -0700 +Subject: [PATCH] Update IPMI Chassis Control command + +This change updates the IPMI Chassis Control command to use the new +host state transitions. This allows each chassis control action +to more closely follow the behavior defined in the IPMI spec. + +ref: https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/22358 + +Tested: +Ran each IPMI chassis control command to confirm the expected +behavior: +ipmitool power on: system is powered-on +ipmitool power off: system is forced off +ipmitool power cycle: system is forced off then powered-on +ipmitool power reset: system is hard reset +ipmitool power soft: soft power-off requested from system software + +Change-Id: Ic9fba3ca4abd9a758eb88f1e6ee09f7ca64ff80a +Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com> +--- + chassishandler.cpp | 204 +++++++++++---------------------------------- + 1 file changed, 48 insertions(+), 156 deletions(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index dfbe004be490..cd0ba3402f84 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -32,6 +32,7 @@ + #include <xyz/openbmc_project/Control/Boot/Source/server.hpp> + #include <xyz/openbmc_project/Control/Boot/Type/server.hpp> + #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp> ++#include <xyz/openbmc_project/State/Chassis/server.hpp> + #include <xyz/openbmc_project/State/Host/server.hpp> + #include <xyz/openbmc_project/State/PowerOnHours/server.hpp> + +@@ -815,59 +816,63 @@ ipmi::RspType<> ipmiSetChassisCap(bool intrusion, bool fpLockout, + //------------------------------------------ + // Calls into Host State Manager Dbus object + //------------------------------------------ +-int initiate_state_transition(State::Host::Transition transition) ++int initiateHostStateTransition(State::Host::Transition transition) + { + // OpenBMC Host State Manager dbus framework +- constexpr auto HOST_STATE_MANAGER_ROOT = "/xyz/openbmc_project/state/host0"; +- constexpr auto HOST_STATE_MANAGER_IFACE = "xyz.openbmc_project.State.Host"; +- constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties"; +- constexpr auto PROPERTY = "RequestedHostTransition"; ++ constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0"; ++ constexpr auto hostStateIntf = "xyz.openbmc_project.State.Host"; + +- // sd_bus error +- int rc = 0; +- char* busname = NULL; ++ auto service = ipmi::getService(*getSdBus(), hostStateIntf, hostStatePath); + +- // SD Bus error report mechanism. +- sd_bus_error bus_error = SD_BUS_ERROR_NULL; ++ // Convert to string equivalent of the passed in transition enum. ++ auto request = State::convertForMessage(transition); + +- // Gets a hook onto either a SYSTEM or SESSION bus +- sd_bus* bus_type = ipmid_get_sd_bus_connection(); +- rc = mapper_get_service(bus_type, HOST_STATE_MANAGER_ROOT, &busname); +- if (rc < 0) ++ try ++ { ++ ipmi::setDbusProperty(*getSdBus(), service, hostStatePath, ++ hostStateIntf, "RequestedHostTransition", ++ request); ++ } ++ catch (std::exception& e) + { + log<level::ERR>( +- "Failed to get bus name", +- entry("ERRNO=0x%X, OBJPATH=%s", -rc, HOST_STATE_MANAGER_ROOT)); +- return rc; ++ "Failed to initiate transition", ++ entry("EXCEPTION=%s, REQUEST=%s", e.what(), request.c_str())); ++ return -1; + } ++ return 0; ++} ++ ++//------------------------------------------ ++// Calls into Chassis State Manager Dbus object ++//------------------------------------------ ++int initiateChassisStateTransition(State::Chassis::Transition transition) ++{ ++ // OpenBMC Chassis State Manager dbus framework ++ constexpr auto chassisStatePath = "/xyz/openbmc_project/state/chassis0"; ++ constexpr auto chassisStateIntf = "xyz.openbmc_project.State.Chassis"; ++ ++ auto service = ++ ipmi::getService(*getSdBus(), chassisStateIntf, chassisStatePath); + + // Convert to string equivalent of the passed in transition enum. + auto request = State::convertForMessage(transition); + +- rc = sd_bus_call_method(bus_type, // On the system bus +- busname, // Service to contact +- HOST_STATE_MANAGER_ROOT, // Object path +- DBUS_PROPERTY_IFACE, // Interface name +- "Set", // Method to be called +- &bus_error, // object to return error +- nullptr, // Response buffer if any +- "ssv", // Takes 3 arguments +- HOST_STATE_MANAGER_IFACE, PROPERTY, "s", +- request.c_str()); +- if (rc < 0) +- { +- log<level::ERR>("Failed to initiate transition", +- entry("ERRNO=0x%X, REQUEST=%s", -rc, request.c_str())); ++ try ++ { ++ ipmi::setDbusProperty(*getSdBus(), service, chassisStatePath, ++ chassisStateIntf, "RequestedPowerTransition", ++ request); + } +- else ++ catch (std::exception& e) + { +- log<level::INFO>("Transition request initiated successfully"); ++ log<level::ERR>( ++ "Failed to initiate transition", ++ entry("EXCEPTION=%s, REQUEST=%s", e.what(), request.c_str())); ++ return -1; + } + +- sd_bus_error_free(&bus_error); +- free(busname); +- +- return rc; ++ return 0; + } + + //------------------------------------------ +@@ -1302,76 +1307,6 @@ ipmi::RspType<uint4_t, // Restart Cause + return ipmi::responseSuccess(cause.value(), reserved, channel); + } + +-//------------------------------------------------------------- +-// Send a command to SoftPowerOff application to stop any timer +-//------------------------------------------------------------- +-int stop_soft_off_timer() +-{ +- constexpr auto iface = "org.freedesktop.DBus.Properties"; +- constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal." +- "SoftPowerOff"; +- +- constexpr auto property = "ResponseReceived"; +- constexpr auto value = "xyz.openbmc_project.Ipmi.Internal." +- "SoftPowerOff.HostResponse.HostShutdown"; +- +- // Get the system bus where most system services are provided. +- auto bus = ipmid_get_sd_bus_connection(); +- +- // Get the service name +- // TODO openbmc/openbmc#1661 - Mapper refactor +- // +- // See openbmc/openbmc#1743 for some details but high level summary is that +- // for now the code will directly call the soft off interface due to a +- // race condition with mapper usage +- // +- // char *busname = nullptr; +- // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname); +- // if (r < 0) +- //{ +- // fprintf(stderr, "Failed to get %s bus name: %s\n", +- // SOFTOFF_OBJPATH, -r); +- // return r; +- //} +- +- // No error object or reply expected. +- int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface, +- "Set", nullptr, nullptr, "ssv", soft_off_iface, +- property, "s", value); +- if (rc < 0) +- { +- log<level::ERR>("Failed to set property in SoftPowerOff object", +- entry("ERRNO=0x%X", -rc)); +- } +- +- // TODO openbmc/openbmc#1661 - Mapper refactor +- // free(busname); +- return rc; +-} +- +-//---------------------------------------------------------------------- +-// Create file to indicate there is no need for softoff notification to host +-//---------------------------------------------------------------------- +-void indicate_no_softoff_needed() +-{ +- fs::path path{HOST_INBAND_REQUEST_DIR}; +- if (!fs::is_directory(path)) +- { +- fs::create_directory(path); +- } +- +- // Add the host instance (default 0 for now) to the file name +- std::string file{HOST_INBAND_REQUEST_FILE}; +- auto size = std::snprintf(nullptr, 0, file.c_str(), 0); +- size++; // null +- std::unique_ptr<char[]> buf(new char[size]); +- std::snprintf(buf.get(), size, file.c_str(), 0); +- +- // Append file name to directory and create it +- path /= buf.get(); +- std::ofstream(path.c_str()); +-} +- + /** @brief Implementation of chassis control command + * + * @param - chassisControl command byte +@@ -1384,66 +1319,22 @@ ipmi::RspType<> ipmiChassisControl(uint8_t chassisControl) + switch (chassisControl) + { + case CMD_POWER_ON: +- rc = initiate_state_transition(State::Host::Transition::On); ++ rc = initiateHostStateTransition(State::Host::Transition::On); + break; + case CMD_POWER_OFF: +- // This path would be hit in 2 conditions. +- // 1: When user asks for power off using ipmi chassis command 0x04 +- // 2: Host asking for power off post shutting down. +- +- // If it's a host requested power off, then need to nudge Softoff +- // application that it needs to stop the watchdog timer if running. +- // If it is a user requested power off, then this is not really +- // needed. But then we need to differentiate between user and host +- // calling this same command +- +- // For now, we are going ahead with trying to nudge the soft off and +- // interpret the failure to do so as a non softoff case +- rc = stop_soft_off_timer(); +- +- // Only request the Off transition if the soft power off +- // application is not running +- if (rc < 0) +- { +- // First create a file to indicate to the soft off application +- // that it should not run. Not doing this will result in State +- // manager doing a default soft power off when asked for power +- // off. +- indicate_no_softoff_needed(); +- +- // Now request the shutdown +- rc = initiate_state_transition(State::Host::Transition::Off); +- } +- else +- { +- log<level::INFO>("Soft off is running, so let shutdown target " +- "stop the host"); +- } ++ rc = ++ initiateChassisStateTransition(State::Chassis::Transition::Off); + break; +- + case CMD_HARD_RESET: +- rc = initiate_state_transition( ++ rc = initiateHostStateTransition( + State::Host::Transition::ForceWarmReboot); + break; + case CMD_POWER_CYCLE: +- // SPEC has a section that says certain implementations can trigger +- // PowerOn if power is Off when a command to power cycle is +- // requested +- +- // First create a file to indicate to the soft off application +- // that it should not run since this is a direct user initiated +- // power reboot request (i.e. a reboot request that is not +- // originating via a soft power off SMS request) +- indicate_no_softoff_needed(); +- +- rc = initiate_state_transition(State::Host::Transition::Reboot); ++ rc = initiateHostStateTransition(State::Host::Transition::Reboot); + break; +- + case CMD_SOFT_OFF_VIA_OVER_TEMP: +- // Request Host State Manager to do a soft power off +- rc = initiate_state_transition(State::Host::Transition::Off); ++ rc = initiateHostStateTransition(State::Host::Transition::Off); + break; +- + case CMD_PULSE_DIAGNOSTIC_INTR: + rc = setNmiProperty(true); + break; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0063-Save-the-pre-timeout-interrupt-in-dbus-property.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0063-Save-the-pre-timeout-interrupt-in-dbus-property.patch new file mode 100644 index 000000000..d0f9331d2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0063-Save-the-pre-timeout-interrupt-in-dbus-property.patch @@ -0,0 +1,140 @@ +From cfb5e13388531e1317eeb3ccf0f8eef0c6eeca60 Mon Sep 17 00:00:00 2001 +From: Ren Yu <yux.ren@intel.com> +Date: Tue, 28 May 2019 17:11:17 +0800 +Subject: [PATCH] Save the pre-timeout interrupt in dbus property + +Get the watchdog pre-timeout interrupt value from ipmi watchdog set command, +and store it into dbus property. + +Tested: +Config IPMI watchdog: BIOS FRB2 Power Cycle after 1 seconds: +ipmitool raw 0x06 0x24 0x01 0x13 0x0 0x2 0xa 0x00 +Start watchdog: +Ipmitool mc watchdog reset +Check the watchdog pre-timeout interrupt in below: +https://BMCIP/redfish/v1/Systems/system/LogServices/EventLog/Entries + +Signed-off-by: Ren Yu <yux.ren@intel.com> +--- + app/watchdog.cpp | 47 ++++++++++++++++++++++++++++++++++++++++ + app/watchdog_service.cpp | 6 +++++ + app/watchdog_service.hpp | 9 ++++++++ + 3 files changed, 62 insertions(+) + +diff --git a/app/watchdog.cpp b/app/watchdog.cpp +index 03c373e..cb0b1fd 100644 +--- a/app/watchdog.cpp ++++ b/app/watchdog.cpp +@@ -80,6 +80,7 @@ ipmi::RspType<> ipmiAppResetWatchdogTimer() + + static constexpr uint8_t wd_dont_stop = 0x1 << 6; + static constexpr uint8_t wd_timeout_action_mask = 0x3; ++static constexpr uint8_t wdPreTimeoutInterruptMask = 0x3; + + static constexpr uint8_t wdTimerUseResTimer1 = 0x0; + static constexpr uint8_t wdTimerUseResTimer2 = 0x6; +@@ -127,6 +128,45 @@ WatchdogService::Action ipmiActionToWdAction(IpmiAction ipmi_action) + } + } + ++enum class IpmiPreTimeoutInterrupt : uint8_t ++{ ++ None = 0x0, ++ SMI = 0x1, ++ NMI = 0x2, ++ MI = 0x3, ++}; ++/** @brief Converts an IPMI Watchdog PreTimeoutInterrupt to DBUS defined action ++ * @param[in] ipmi_action The IPMI Watchdog PreTimeoutInterrupt ++ * @return The Watchdog PreTimeoutInterrupt that the ipmi_action maps to ++ */ ++WatchdogService::PreTimeoutInterruptAction ipmiPreTimeoutInterruptToWdAction( ++ IpmiPreTimeoutInterrupt ipmiPreTimeOutInterrupt) ++{ ++ switch (ipmiPreTimeOutInterrupt) ++ { ++ case IpmiPreTimeoutInterrupt::None: ++ { ++ return WatchdogService::PreTimeoutInterruptAction::None; ++ } ++ case IpmiPreTimeoutInterrupt::SMI: ++ { ++ return WatchdogService::PreTimeoutInterruptAction::SMI; ++ } ++ case IpmiPreTimeoutInterrupt::NMI: ++ { ++ return WatchdogService::PreTimeoutInterruptAction::NMI; ++ } ++ case IpmiPreTimeoutInterrupt::MI: ++ { ++ return WatchdogService::PreTimeoutInterruptAction::MI; ++ } ++ default: ++ { ++ throw std::domain_error("IPMI PreTimeoutInterrupt is invalid"); ++ } ++ } ++} ++ + enum class IpmiTimerUse : uint8_t + { + Reserved = 0x0, +@@ -250,6 +290,13 @@ ipmi::RspType<> + // Mark as initialized so that future resets behave correctly + wd_service.setInitialized(true); + ++ // pretimeOutAction ++ const auto ipmiPreTimeoutInterrupt = ++ static_cast<IpmiPreTimeoutInterrupt>(wdPreTimeoutInterruptMask & ++ (static_cast<uint8_t>(preTimeoutInterrupt))); ++ wd_service.setPreTimeoutInterrupt( ++ ipmiPreTimeoutInterruptToWdAction(ipmiPreTimeoutInterrupt)); ++ + lastCallSuccessful = true; + return ipmi::responseSuccess(); + } +diff --git a/app/watchdog_service.cpp b/app/watchdog_service.cpp +index 3534e89..4df1ab6 100644 +--- a/app/watchdog_service.cpp ++++ b/app/watchdog_service.cpp +@@ -198,3 +198,9 @@ void WatchdogService::setInterval(uint64_t interval) + { + setProperty("Interval", interval); + } ++ ++void WatchdogService::setPreTimeoutInterrupt( ++ PreTimeoutInterruptAction preTimeoutInterrupt) ++{ ++ setProperty("PreTimeoutInterrupt", convertForMessage(preTimeoutInterrupt)); ++} +\ No newline at end of file +diff --git a/app/watchdog_service.hpp b/app/watchdog_service.hpp +index 141bdb7..32b7461 100644 +--- a/app/watchdog_service.hpp ++++ b/app/watchdog_service.hpp +@@ -15,6 +15,8 @@ class WatchdogService + + using Action = + sdbusplus::xyz::openbmc_project::State::server::Watchdog::Action; ++ using PreTimeoutInterruptAction = sdbusplus::xyz::openbmc_project::State:: ++ server::Watchdog::PreTimeoutInterruptAction; + using TimerUse = + sdbusplus::xyz::openbmc_project::State::server::Watchdog::TimerUse; + +@@ -92,6 +94,13 @@ class WatchdogService + */ + void setInterval(uint64_t interval); + ++ /** @brief Sets the value of the PreTimeoutInterrupt property on the host ++ * watchdog ++ * ++ * @param[in] PreTimeoutInterrupt - The new PreTimeoutInterrupt value ++ */ ++ void setPreTimeoutInterrupt(PreTimeoutInterruptAction preTimeoutInterrupt); ++ + private: + /** @brief sdbusplus handle */ + sdbusplus::bus::bus bus; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/phosphor-ipmi-host.service b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/phosphor-ipmi-host.service new file mode 100644 index 000000000..1e45ee6c9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/phosphor-ipmi-host.service @@ -0,0 +1,15 @@ +[Unit] +Description=Phosphor Inband IPMI + +[Service] +Restart=always +RestartSec=5 +StartLimitBurst=10 +ExecStart=/usr/bin/env ipmid +SyslogIdentifier=ipmid +RuntimeDirectory = ipmi +RuntimeDirectoryPreserve = yes +StateDirectory = ipmi + +[Install] +WantedBy=basic.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/transporthandler_oem.cpp b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/transporthandler_oem.cpp new file mode 100644 index 000000000..856a80fbc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/transporthandler_oem.cpp @@ -0,0 +1,147 @@ +/* Copyright 2019 Intel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dcmihandler.hpp" + +#include <cstdint> +#include <ipmid/api-types.hpp> +#include <ipmid/api.hpp> +#include <ipmid/message.hpp> +#include <ipmid/message/types.hpp> +#include <ipmid/utils.hpp> +#include <vector> + +enum class oemLanParam : uint8_t +{ + intelHostnameConfig = 0xc7, +}; + +constexpr size_t IpmiHostnameLen = 16; +constexpr uint8_t CurrentRevision = 0x11; // Current rev per IPMI Spec 2.0 + +constexpr ipmi::Cc ccParamNotSupported = 0x80; +constexpr ipmi::Cc ccUnprintable = 0x90; + +namespace ipmi::transport +{ + +constexpr auto validHostnameChars = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX" + "YZ0123456789-"; +constexpr int lanOemHostnameLength = 64; + +RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req) +{ + std::vector<uint8_t> dataBytes; + switch (static_cast<oemLanParam>(parameter)) + { + case oemLanParam::intelHostnameConfig: + { + static std::array<uint8_t, lanOemHostnameLength> blockData; + uint8_t block = 0; + uint8_t complete = 0; + if ((req.unpack(block, complete, dataBytes) != 0) || + (!req.fullyUnpacked())) + { + return responseReqDataLenInvalid(); + } + + size_t numDataBytes = req.size() - 4; + if ((numDataBytes > IpmiHostnameLen) || + (!complete && (numDataBytes < IpmiHostnameLen))) + { + return responseReqDataLenInvalid(); + } + + if (!((block > 0) && (block < 5)) || + ((complete != 0) && (complete != 1))) + { + return responseInvalidFieldRequest(); + } + + if (block == 1) + { + blockData.fill(0); + } + + std::copy(dataBytes.begin(), dataBytes.end(), + blockData.data() + ((block - 1) * IpmiHostnameLen)); + if (complete) + { + blockData[lanOemHostnameLength - 1] = 0; + // check hostname, and write it + std::string newHostname( + reinterpret_cast<char*>(blockData.data()), + lanOemHostnameLength); + size_t firstNull = newHostname.find_first_of('\0'); + if (newHostname.find_first_not_of(validHostnameChars) != + firstNull) + { + return response(ccUnprintable); + } + std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); + ipmi::setDbusProperty(*busp, ::dcmi::networkServiceName, + ::dcmi::networkConfigObj, + ::dcmi::networkConfigIntf, + ::dcmi::hostNameProp, newHostname); + } + return responseSuccess(); + } + default: + return response(ccParamNotSupported); + } + return response(ccParamNotSupported); +} + +RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter, + uint8_t set, uint8_t block) +{ + message::Payload message; + message.pack(CurrentRevision); + oemLanParam param = static_cast<oemLanParam>(parameter); + switch (param) + { + case oemLanParam::intelHostnameConfig: + { + if (set != 0) + { + return responseInvalidFieldRequest(); + } + if ((block < 1) || (block > 4)) + { + return responseInvalidFieldRequest(); + } + std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); + auto service = ipmi::getService(*busp, ::dcmi::networkConfigIntf, + ::dcmi::networkConfigObj); + auto value = ipmi::getDbusProperty( + *busp, service, ::dcmi::networkConfigObj, + ::dcmi::networkConfigIntf, ::dcmi::hostNameProp); + std::string hostname = std::get<std::string>(value); + std::array<char, IpmiHostnameLen> buf = {0}; + size_t head = (block - 1) * IpmiHostnameLen; + if (head < hostname.size()) + { + size_t numToCopy = hostname.size() - head; + numToCopy = std::min(IpmiHostnameLen, numToCopy); + hostname.copy(buf.data(), numToCopy, head); + } + message.pack(buf); + return responseSuccess(std::move(message)); + } + } + return response(ccParamNotSupported); +} +} // namespace ipmi::transport diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend new file mode 100644 index 000000000..87aab6f79 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend @@ -0,0 +1,38 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" +PROJECT_SRC_DIR := "${THISDIR}/${PN}" + +#SRC_URI += "git://github.com/openbmc/phosphor-host-ipmid" +SRCREV = "78fe1b14f60d55ae335369fb2c6e81ed9ac6d865" + +SRC_URI += "file://phosphor-ipmi-host.service \ + file://0010-fix-get-system-GUID-ipmi-command.patch \ + file://0053-Fix-keep-looping-issue-when-entering-OS.patch \ + file://0059-Move-Set-SOL-config-parameter-to-host-ipmid.patch \ + file://0060-Move-Get-SOL-config-parameter-to-host-ipmid.patch \ + file://0062-Update-IPMI-Chassis-Control-command.patch \ + file://0063-Save-the-pre-timeout-interrupt-in-dbus-property.patch \ + " + +EXTRA_OECONF:append = " --disable-i2c-whitelist-check" +EXTRA_OECONF:append = " --enable-transport-oem=yes" +EXTRA_OECONF:append = " --disable-boot-flag-safe-mode-support" +EXTRA_OECONF:append = " --disable-ipmi-whitelist" + +RDEPENDS:${PN}:remove = "clear-once" + +# remove the softpoweroff service since we do not need it +SYSTEMD_SERVICE:${PN}:remove = " \ + xyz.openbmc_project.Ipmi.Internal.SoftPowerOff.service" + +SYSTEMD_LINK:${PN}:remove = " \ + ../xyz.openbmc_project.Ipmi.Internal.SoftPowerOff.service:obmc-host-shutdown@0.target.requires/xyz.openbmc_project.Ipmi.Internal.SoftPowerOff.service \ + " + +do_compile:prepend(){ + cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S} +} + +do_install:append(){ + rm -f ${D}/${bindir}/phosphor-softpoweroff + rm -f ${S}/transporthandler_oem.cpp +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/0001-Add-dbus-method-SlotIpmbRequest.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/0001-Add-dbus-method-SlotIpmbRequest.patch new file mode 100644 index 000000000..d071ebd67 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/0001-Add-dbus-method-SlotIpmbRequest.patch @@ -0,0 +1,535 @@ +From 42231615d6a1effbfaa581ca41c0d406174feee8 Mon Sep 17 00:00:00 2001 +From: Rajashekar Gade Reddy <raja.sekhar.reddy.gade@linux.intel.com> +Date: Mon, 23 Mar 2020 22:19:07 +0530 +Subject: [PATCH] Add dbus method SlotIpmbRequest + +Added dbus method SlotIpmbRequest which enables the applications to +communicate with add-in cards. + +This is submitted in down stream because SlotIpmbRequest uses hold and +unhold mux kernel patches which are downstream only patches. + +Tested: + +busctl call xyz.openbmc_project.Ipmi.Channel.Ipmb +/xyz/openbmc_project/Ipmi/Channel/Ipmb org.openbmc.Ipmb SlotIpmbRequest +"yyyyyay" <valid_addressType> <valid_slot> <valid_slaveAddr> <valid_netFun> <valid_cmd> <data> // method call +(iyyyyay) 0 7 0 1 0 15 0 0 0 0 2 12 87 1 0 0 0 0 0 0 0 // success + +busctl call xyz.openbmc_project.Ipmi.Channel.Ipmb +/xyz/openbmc_project/Ipmi/Channel/Ipmb org.openbmc.Ipmb SlotIpmbRequest +"yyyyyay" <valid_addressType> <invalid_slot> <valid_slaveAddr> <valid_netFun> <valid_cmd> <data> // method call +(iyyyyay) 4 0 0 0 0 0 // failure + +busctl call xyz.openbmc_project.Ipmi.Channel.Ipmb +/xyz/openbmc_project/Ipmi/Channel/Ipmb org.openbmc.Ipmb SlotIpmbRequest +"yyyyyay" <valid_addressType> <valid_slot> <invalid_slaveAddr> <valid_netFun> <valid_cmd> <data> // method call +(iyyyyay) 4 0 0 0 0 0 // failure + +//This ipmi command internally calls the dbus method SlotIpmbRequest. +ipmitool raw 0x3e 0x51 0 0x01 0xb0 0x6 1 +00 00 00 00 00 02 0c 57 01 00 00 00 00 00 00 00 //success + +Note: Tested for all possible negative test cases and it works fine. + +Signed-off-by: Rajashekar Gade Reddy <raja.sekhar.reddy.gade@intel.com> +--- + CMakeLists.txt | 2 +- + include/linux/i2c.h | 159 +++++++++++++++++++++++++++++++ + ipmb-channels.json | 6 ++ + ipmbbridged.cpp | 221 +++++++++++++++++++++++++++++++++++++++++++- + ipmbbridged.hpp | 8 +- + 5 files changed, 392 insertions(+), 4 deletions(-) + create mode 100644 include/linux/i2c.h + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 4acdccf..3484a58 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -8,7 +8,7 @@ set (CMAKE_CXX_STANDARD_REQUIRED ON) + #set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") + #set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti") + +-include_directories (${CMAKE_CURRENT_SOURCE_DIR}) ++include_directories (${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include) + find_package (Boost REQUIRED) + include_directories (${Boost_INCLUDE_DIRS}) + add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY) +diff --git a/include/linux/i2c.h b/include/linux/i2c.h +new file mode 100644 +index 0000000..a1db9b1 +--- /dev/null ++++ b/include/linux/i2c.h +@@ -0,0 +1,159 @@ ++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ ++/* ------------------------------------------------------------------------- */ ++/* */ ++/* i2c.h - definitions for the i2c-bus interface */ ++/* */ ++/* ------------------------------------------------------------------------- */ ++/* Copyright (C) 1995-2000 Simon G. Vogl ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++ MA 02110-1301 USA. */ ++/* ------------------------------------------------------------------------- */ ++ ++/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and ++ Frodo Looijaard <frodol@dds.nl> */ ++ ++#ifndef _UAPI_LINUX_I2C_H ++#define _UAPI_LINUX_I2C_H ++ ++#include <linux/types.h> ++ ++/** ++ * struct i2c_msg - an I2C transaction segment beginning with START ++ * @addr: Slave address, either seven or ten bits. When this is a ten ++ * bit address, I2C_M_TEN must be set in @flags and the adapter ++ * must support I2C_FUNC_10BIT_ADDR. ++ * @flags: I2C_M_RD is handled by all adapters. No other flags may be ++ * provided unless the adapter exported the relevant I2C_FUNC_* ++ * flags through i2c_check_functionality(). ++ * @len: Number of data bytes in @buf being read from or written to the ++ * I2C slave address. For read transactions where I2C_M_RECV_LEN ++ * is set, the caller guarantees that this buffer can hold up to ++ * 32 bytes in addition to the initial length byte sent by the ++ * slave (plus, if used, the SMBus PEC); and this value will be ++ * incremented by the number of block data bytes received. ++ * @buf: The buffer into which data is read, or from which it's written. ++ * ++ * An i2c_msg is the low level representation of one segment of an I2C ++ * transaction. It is visible to drivers in the @i2c_transfer() procedure, ++ * to userspace from i2c-dev, and to I2C adapter drivers through the ++ * @i2c_adapter.@master_xfer() method. ++ * ++ * Except when I2C "protocol mangling" is used, all I2C adapters implement ++ * the standard rules for I2C transactions. Each transaction begins with a ++ * START. That is followed by the slave address, and a bit encoding read ++ * versus write. Then follow all the data bytes, possibly including a byte ++ * with SMBus PEC. The transfer terminates with a NAK, or when all those ++ * bytes have been transferred and ACKed. If this is the last message in a ++ * group, it is followed by a STOP. Otherwise it is followed by the next ++ * @i2c_msg transaction segment, beginning with a (repeated) START. ++ * ++ * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then ++ * passing certain @flags may have changed those standard protocol behaviors. ++ * Those flags are only for use with broken/nonconforming slaves, and with ++ * adapters which are known to support the specific mangling options they ++ * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR). ++ */ ++struct i2c_msg { ++ __u16 addr; /* slave address */ ++ __u16 flags; ++#define I2C_M_RD 0x0001 /* read data, from slave to master */ ++ /* I2C_M_RD is guaranteed to be 0x0001! */ ++#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ ++#define I2C_M_HOLD 0x0100 /* for holding a mux path */ ++#define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */ ++ /* makes only sense in kernelspace */ ++ /* userspace buffers are copied anyway */ ++#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ ++#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ ++#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ ++#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ ++#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ ++#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ ++ __u16 len; /* msg length */ ++ __u8 *buf; /* pointer to msg data */ ++}; ++ ++/* To determine what functionality is present */ ++ ++#define I2C_FUNC_I2C 0x00000001 ++#define I2C_FUNC_10BIT_ADDR 0x00000002 ++#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_IGNORE_NAK etc. */ ++#define I2C_FUNC_SMBUS_PEC 0x00000008 ++#define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */ ++#define I2C_FUNC_SLAVE 0x00000020 ++#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */ ++#define I2C_FUNC_SMBUS_QUICK 0x00010000 ++#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 ++#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 ++#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 ++#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 ++#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 ++#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 ++#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 ++#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 ++#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 ++#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */ ++#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */ ++#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000 ++ ++#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \ ++ I2C_FUNC_SMBUS_WRITE_BYTE) ++#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \ ++ I2C_FUNC_SMBUS_WRITE_BYTE_DATA) ++#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \ ++ I2C_FUNC_SMBUS_WRITE_WORD_DATA) ++#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ ++ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) ++#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ ++ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) ++ ++#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | \ ++ I2C_FUNC_SMBUS_BYTE | \ ++ I2C_FUNC_SMBUS_BYTE_DATA | \ ++ I2C_FUNC_SMBUS_WORD_DATA | \ ++ I2C_FUNC_SMBUS_PROC_CALL | \ ++ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \ ++ I2C_FUNC_SMBUS_I2C_BLOCK | \ ++ I2C_FUNC_SMBUS_PEC) ++ ++/* ++ * Data for SMBus Messages ++ */ ++#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ ++union i2c_smbus_data { ++ __u8 byte; ++ __u16 word; ++ __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ ++ /* and one more for user-space compatibility */ ++}; ++ ++/* i2c_smbus_xfer read or write markers */ ++#define I2C_SMBUS_READ 1 ++#define I2C_SMBUS_WRITE 0 ++ ++/* SMBus transaction types (size parameter in the above functions) ++ Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ ++#define I2C_SMBUS_QUICK 0 ++#define I2C_SMBUS_BYTE 1 ++#define I2C_SMBUS_BYTE_DATA 2 ++#define I2C_SMBUS_WORD_DATA 3 ++#define I2C_SMBUS_PROC_CALL 4 ++#define I2C_SMBUS_BLOCK_DATA 5 ++#define I2C_SMBUS_I2C_BLOCK_BROKEN 6 ++#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ ++#define I2C_SMBUS_I2C_BLOCK_DATA 8 ++ ++#endif /* _UAPI_LINUX_I2C_H */ +diff --git a/ipmb-channels.json b/ipmb-channels.json +index 0876db7..ff570c6 100644 +--- a/ipmb-channels.json ++++ b/ipmb-channels.json +@@ -11,6 +11,12 @@ + "slave-path": "/dev/ipmb-0", + "bmc-addr": 32, + "remote-addr": 88 ++ }, ++ { ++ "type": "slot-ipmb", ++ "slave-path": "/dev/ipmb-6", ++ "bmc-addr": 18, ++ "remote-addr": 176 + } + ] + } +diff --git a/ipmbbridged.cpp b/ipmbbridged.cpp +index 3bf8469..6d1be04 100644 +--- a/ipmbbridged.cpp ++++ b/ipmbbridged.cpp +@@ -18,6 +18,11 @@ + #include "ipmbdefines.hpp" + #include "ipmbutils.hpp" + ++#include <i2c/smbus.h> ++#include <linux/i2c-dev.h> ++#include <linux/i2c.h> ++#include <sys/ioctl.h> ++ + #include <boost/algorithm/string/replace.hpp> + #include <boost/asio/write.hpp> + #include <filesystem> +@@ -40,7 +45,8 @@ auto conn = std::make_shared<sdbusplus::asio::connection>(io); + static std::list<IpmbChannel> ipmbChannels; + static const std::unordered_map<std::string, ipmbChannelType> + ipmbChannelTypeMap = {{"me", ipmbChannelType::me}, +- {"ipmb", ipmbChannelType::ipmb}}; ++ {"ipmb", ipmbChannelType::ipmb}, ++ {"slot-ipmb", ipmbChannelType::slot_ipmb}}; + + /** + * @brief Ipmb request class methods +@@ -555,7 +561,10 @@ int IpmbChannel::ipmbChannelInit(const char *ipmbI2cSlave) + { + std::string deviceFileName = + "/sys/bus/i2c/devices/i2c-" + busStr + "/new_device"; +- std::string para = "ipmb-dev 0x1010"; // init with BMC addr 0x20 ++ std::ostringstream param; ++ param << "ipmb-dev 0x" << std::hex ++ << static_cast<uint16_t>(0x1000 | (ipmbBmcSlaveAddress >> 1)); ++ std::string para(param.str()); + std::fstream deviceFile; + deviceFile.open(deviceFileName, std::ios::out); + if (!deviceFile.good()) +@@ -711,6 +720,171 @@ void IpmbChannel::addFilter(const uint8_t respNetFn, const uint8_t cmd) + } + } + ++class Mux ++{ ++ public: ++ Mux(const std::string &path) : heldMux(false) ++ { ++ fd = open(path.c_str(), O_RDWR | O_NONBLOCK); ++ } ++ ~Mux() ++ { ++ if (heldMux) ++ { ++ if (unholdMux() < 0) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error while unholding the bus"); ++ } ++ } ++ if (!(fd < 0)) ++ { ++ close(fd); ++ } ++ } ++ ++ int transferAndHoldMux(const uint8_t slaveAddr, uint8_t *buffer, ++ const uint8_t len, uint16_t timeout) ++ { ++ if (!isMuxFdOpen()) ++ { ++ return -1; ++ } ++ struct i2c_msg holdmsg[2] = { ++ {slaveAddr, 0, len, buffer}, ++ {0, I2C_M_HOLD, sizeof(timeout), (uint8_t *)&timeout}}; ++ ++ struct i2c_rdwr_ioctl_data msgrdwr = {&holdmsg[0], 2}; ++ ++ int retVal = ioctl(fd, I2C_RDWR, &msgrdwr); ++ if (retVal >= 0) ++ { ++ heldMux = true; ++ } ++ return retVal; ++ } ++ ++ bool isMuxFdOpen() ++ { ++ if (fd < 0) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error while opening the mux device file"); ++ return false; ++ } ++ return true; ++ } ++ ++ private: ++ int unholdMux() ++ { ++ if (!isMuxFdOpen()) ++ { ++ return -1; ++ } ++ uint16_t holdtimeout = 0; // unhold the bus ++ ++ struct i2c_msg holdmsg = {0, I2C_M_HOLD, sizeof(holdtimeout), ++ (uint8_t *)&holdtimeout}; ++ ++ struct i2c_rdwr_ioctl_data msgrdwr = {&holdmsg, 1}; ++ ++ return ioctl(fd, I2C_RDWR, &msgrdwr); ++ } ++ ++ int fd; ++ bool heldMux; ++}; ++ ++std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> ++ IpmbChannel::slotRequestAdd(boost::asio::yield_context &yield, ++ std::shared_ptr<IpmbRequest> request, ++ const uint8_t pcieSlot) ++{ ++ makeRequestValid(request); ++ std::filesystem::path p = ++ "/dev/i2c-mux/PCIE_Mux/Pcie_Slot_" + std::to_string(pcieSlot); ++ ++ if (!std::filesystem::exists(p) || !std::filesystem::is_symlink(p)) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "does not exist or not a symlink ", ++ phosphor::logging::entry("File:%s", p.c_str())); ++ return returnStatus(ipmbResponseStatus::error); ++ } ++ ++ Mux mux(p); ++ if (!mux.isMuxFdOpen()) ++ { ++ return returnStatus(ipmbResponseStatus::error); ++ } ++ ++ std::vector<uint8_t> buffer(0); ++ if (request->ipmbToi2cConstruct(buffer) != 0) ++ { ++ return returnStatus(ipmbResponseStatus::error); ++ } ++ ++ uint8_t size = buffer.size(); ++ ++ const uint8_t slaveAddrIndex = 1; ++ const uint8_t slotIpmbHeader = 2; ++ ++ for (int i = 0; i < ipmbNumberOfTries; i++) ++ { ++ boost::system::error_code ec; ++ int i2cRetryCnt = 0; ++ do ++ { ++ if (mux.transferAndHoldMux( ++ buffer[slaveAddrIndex] >> 1, buffer.data() + slotIpmbHeader, ++ size - slotIpmbHeader, ipmbRequestRetryTimeout) >= 0) ++ { ++ break; ++ } ++ ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error sending slot IPMB command"); ++ i2cRetryCnt++; ++ } while (i2cRetryCnt < ipmbI2cNumberOfRetries); ++ ++ if (i2cRetryCnt == ipmbI2cNumberOfRetries) ++ { ++ std::string msgToLog = ++ "slotRequestAdd: Sent to I2C failed after retries." ++ " busId=" + ++ std::to_string(ipmbBusId) + ", error=" + ec.message(); ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ msgToLog.c_str()); ++ makeRequestInvalid(*request); ++ return returnStatus(ipmbResponseStatus::error); ++ } ++ ++ request->timer->expires_after( ++ std::chrono::milliseconds(ipmbRequestRetryTimeout)); ++ request->timer->async_wait(yield[ec]); ++ ++ if (ec && ec != boost::asio::error::operation_aborted) ++ { ++ // unexpected error - invalidate request and return generic error ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "requestAdd: async_wait error"); ++ makeRequestInvalid(*request); ++ return returnStatus(ipmbResponseStatus::error); ++ } ++ ++ if (request->state == ipmbRequestState::matched) ++ { ++ // matched response, send it to client application ++ makeRequestInvalid(*request); ++ return request->returnMatchedResponse(); ++ } ++ } ++ ++ makeRequestInvalid(*request); ++ return returnStatus(ipmbResponseStatus::timeout); ++} ++ + std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> + IpmbChannel::requestAdd(boost::asio::yield_context &yield, + std::shared_ptr<IpmbRequest> request) +@@ -848,6 +1022,47 @@ static int initializeChannels() + return 0; + } + ++auto slotIpmbHandleRequest = ++ [](boost::asio::yield_context yield, uint8_t addressType, ++ uint8_t slotNumber, uint8_t targetSlaveAddr, uint8_t netfn, uint8_t cmd, ++ std::vector<uint8_t> dataReceived) { ++ uint8_t lun = 0; // No support for lun in slot IPMB ++ IpmbChannel *channel = ++ getChannel(static_cast<uint8_t>(ipmbChannelType::slot_ipmb)); ++ if (channel == nullptr) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "slotIpmbHandleRequest: Slot IPMB channel does not exist"); ++ return returnStatus(ipmbResponseStatus::error); ++ } ++ ++ // check outstanding request list for valid sequence number ++ uint8_t seqNum = 0; ++ bool seqValid = channel->seqNumGet(seqNum); ++ if (!seqValid) ++ { ++ phosphor::logging::log<phosphor::logging::level::WARNING>( ++ "slotIpmbHandleRequest: cannot add more requests to the list"); ++ return returnStatus(ipmbResponseStatus::busy); ++ } ++ ++ uint8_t bmcSlaveAddress = channel->getBmcSlaveAddress(); ++ uint8_t rqSlaveAddress = targetSlaveAddr; ++ ++ // construct the request to add it to outstanding request list ++ std::shared_ptr<IpmbRequest> request = std::make_shared<IpmbRequest>( ++ rqSlaveAddress, netfn, ipmbRsLun, bmcSlaveAddress, seqNum, lun, cmd, ++ dataReceived); ++ ++ if (!request->timer) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "slotIpmbHandleRequest: timer object does not exist"); ++ return returnStatus(ipmbResponseStatus::error); ++ } ++ return channel->slotRequestAdd(yield, request, slotNumber); ++ }; ++ + auto ipmbHandleRequest = [](boost::asio::yield_context yield, + uint8_t reqChannel, uint8_t netfn, uint8_t lun, + uint8_t cmd, std::vector<uint8_t> dataReceived) { +@@ -994,6 +1209,8 @@ int main(int argc, char *argv[]) + server.add_interface(ipmbObj, ipmbDbusIntf); + + ipmbIface->register_method("sendRequest", std::move(ipmbHandleRequest)); ++ ipmbIface->register_method("SlotIpmbRequest", ++ std::move(slotIpmbHandleRequest)); + ipmbIface->initialize(); + + if (initializeChannels() < 0) +diff --git a/ipmbbridged.hpp b/ipmbbridged.hpp +index 052c193..c79ac63 100644 +--- a/ipmbbridged.hpp ++++ b/ipmbbridged.hpp +@@ -155,7 +155,8 @@ enum class ipmbRequestState + enum class ipmbChannelType + { + ipmb = 0, +- me = 1 ++ me = 1, ++ slot_ipmb = 2 + }; + + /** +@@ -293,6 +294,11 @@ class IpmbChannel + void ipmbSendI2cFrame(std::shared_ptr<std::vector<uint8_t>> buffer, + size_t retriesAttempted); + ++ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> ++ slotRequestAdd(boost::asio::yield_context &yield, ++ std::shared_ptr<IpmbRequest> requestToSend, ++ const uint8_t pcieSlot); ++ + std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> + requestAdd(boost::asio::yield_context &yield, + std::shared_ptr<IpmbRequest> requestToSend); +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/0002-Add-log-count-limitation-to-requestAdd.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/0002-Add-log-count-limitation-to-requestAdd.patch new file mode 100644 index 000000000..f24d585a9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/0002-Add-log-count-limitation-to-requestAdd.patch @@ -0,0 +1,94 @@ +From 1b01638d44ebb5d6163899775dea2fcc7e0715d6 Mon Sep 17 00:00:00 2001 +From: Helen Huang <he.huang@intel.com> +Date: Mon, 31 May 2021 09:19:55 +0800 +Subject: [PATCH] Add log count limitation to requestAdd() + +To avoid log storm, add the log count limitation to +requestAdd(). + +Change-Id: I91894ff07fa252ed7746816535611a33b6f640ea +Signed-off-by: Helen Huang <he.huang@intel.com> +--- + ipmbbridged.cpp | 44 ++++++++++++++++++++++++++++++++++++++------ + ipmbbridged.hpp | 3 +++ + 2 files changed, 41 insertions(+), 6 deletions(-) + +diff --git a/ipmbbridged.cpp b/ipmbbridged.cpp +index 6d1be04..93f5b2f 100644 +--- a/ipmbbridged.cpp ++++ b/ipmbbridged.cpp +@@ -916,12 +916,44 @@ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> + + if (i2cRetryCnt == ipmbI2cNumberOfRetries) + { +- std::string msgToLog = +- "requestAdd: Sent to I2C failed after retries." +- " busId=" + +- std::to_string(ipmbBusId) + ", error=" + ec.message(); +- phosphor::logging::log<phosphor::logging::level::INFO>( +- msgToLog.c_str()); ++ if ((requestAddLogCount <= ipmbRequestAddLogLimit) || ++ (!(requestAddLogCount % ipmbRequestAddLogInterval)) || ++ (UINT_MAX == requestAddLogCount)) ++ { ++ std::string msgToLog; ++ if (requestAddLogCount == ipmbRequestAddLogLimit) ++ { ++ msgToLog = "requestAdd: There are " + ++ std::to_string(ipmbRequestAddLogLimit - 1) + ++ " similiar logs." ++ " To avoid log storm, not all the logs for the " ++ "issue will be shown: "; ++ } ++ if (!(requestAddLogCount % ipmbRequestAddLogInterval) && (requestAddLogCount != 0)) ++ { ++ msgToLog = "requestAdd: There are " + ++ std::to_string(requestAddLogCount) + ++ " similiar logs so far: "; ++ } ++ if (UINT_MAX == requestAddLogCount) ++ { ++ msgToLog = "requestAdd: There are " + ++ std::to_string(requestAddLogCount) + ++ " similiar logs," ++ " The log count will be rolled back to zero: "; ++ } ++ msgToLog += "requestAdd: Sent to I2C failed after retries." ++ " busId=" + ++ std::to_string(ipmbBusId) + ++ ", error=" + ec.message(); ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ msgToLog.c_str()); ++ } ++ requestAddLogCount++; ++ } ++ else ++ { ++ requestAddLogCount = 0; + } + + request->timer->expires_after( +diff --git a/ipmbbridged.hpp b/ipmbbridged.hpp +index c79ac63..eaba7ae 100644 +--- a/ipmbbridged.hpp ++++ b/ipmbbridged.hpp +@@ -50,6 +50,8 @@ constexpr int ipmbMaxOutstandingRequestsCount = 64; + constexpr int ipmbNumberOfTries = 6; + constexpr uint64_t ipmbRequestRetryTimeout = 250; // ms + ++constexpr int ipmbRequestAddLogLimit = 10; ++constexpr int ipmbRequestAddLogInterval = 100; + /** + * @brief Ipmb I2C communication + */ +@@ -313,6 +315,7 @@ class IpmbChannel + uint8_t ipmbBusId; + uint8_t channelIdx; + ++ unsigned int requestAddLogCount = 0; + std::shared_ptr<IpmbCommandFilter> commandFilter; + + // array storing outstanding requests +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/0003-Fix-for-clearing-outstanding-requests.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/0003-Fix-for-clearing-outstanding-requests.patch new file mode 100644 index 000000000..ffa3ab1b0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/0003-Fix-for-clearing-outstanding-requests.patch @@ -0,0 +1,62 @@ +From e107bdc375aa23a4c0a3d23978cfe8b378bce09a Mon Sep 17 00:00:00 2001 +From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +Date: Mon, 20 Sep 2021 14:06:05 +0000 +Subject: [PATCH] Fix for clearing outstanding requests + +IPMB requests were made valid even if there was an error in opening the +mux bus. This fix makes the IPMB requests valid only we were able to +successfully open the Mux fd. + +Tested: +Verified using busctl command. +Command: busctl call xyz.openbmc_project.Ipmi.Channel.Ipmb + /xyz/openbmc_project/Ipmi/Channel/Ipmb org.openbmc.Ipmb + sendRequest yyyyay 0 0x30 0 0x70 2 0 32 +Response: (iyyyyay) 0 49 0 112 193 0 + +Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +--- + ipmbbridged.cpp | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/ipmbbridged.cpp b/ipmbbridged.cpp +index 3ffc1f7..0508fcc 100644 +--- a/ipmbbridged.cpp ++++ b/ipmbbridged.cpp +@@ -801,7 +801,6 @@ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> + std::shared_ptr<IpmbRequest> request, + const uint8_t pcieSlot) + { +- makeRequestValid(request); + std::filesystem::path p = + "/dev/i2c-mux/PCIE_Mux/Pcie_Slot_" + std::to_string(pcieSlot); + +@@ -825,6 +824,7 @@ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> + return returnStatus(ipmbResponseStatus::error); + } + ++ makeRequestValid(request); + uint8_t size = buffer.size(); + + const uint8_t slaveAddrIndex = 1; +@@ -894,6 +894,7 @@ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> + std::vector<uint8_t> buffer(0); + if (request->ipmbToi2cConstruct(buffer) != 0) + { ++ makeRequestInvalid(*request); + return returnStatus(ipmbResponseStatus::error); + } + +@@ -929,7 +930,8 @@ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>> + " To avoid log storm, not all the logs for the " + "issue will be shown: "; + } +- if (!(requestAddLogCount % ipmbRequestAddLogInterval) && (requestAddLogCount != 0)) ++ if (!(requestAddLogCount % ipmbRequestAddLogInterval) && ++ (requestAddLogCount != 0)) + { + msgToLog = "requestAdd: There are " + + std::to_string(requestAddLogCount) + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/ipmb-channels.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/ipmb-channels.json new file mode 100644 index 000000000..2d77aa6e7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb/ipmb-channels.json @@ -0,0 +1,22 @@ +{ + "channels": [ + { + "type": "me", + "slave-path": "/dev/ipmb-5", + "bmc-addr": 32, + "remote-addr": 44 + }, + { + "type": "ipmb", + "slave-path": "/dev/ipmb-13", + "bmc-addr": 32, + "remote-addr": 32 + }, + { + "type": "slot-ipmb", + "slave-path": "/dev/ipmb-6", + "bmc-addr": 18, + "remote-addr": 176 + } + ] +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend new file mode 100644 index 000000000..caf25fdd6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend @@ -0,0 +1,13 @@ +SRC_URI = "git://github.com/openbmc/ipmbbridge.git" +SRCREV = "8227626764edf13350c5f5a5857298a905fb43f7" +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" +SRC_URI += "file://0001-Add-dbus-method-SlotIpmbRequest.patch \ + file://0002-Add-log-count-limitation-to-requestAdd.patch \ + file://0003-Fix-for-clearing-outstanding-requests.patch \ + file://ipmb-channels.json \ + " + +do_install:append() { + install -D ${WORKDIR}/ipmb-channels.json \ + ${D}/usr/share/ipmbbridge +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/99-ipmi-kcs.rules b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/99-ipmi-kcs.rules new file mode 100644 index 000000000..0a64b58db --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/99-ipmi-kcs.rules @@ -0,0 +1,2 @@ +KERNEL=="ipmi-kcs3", SYMLINK+="ipmi_kcs3" +KERNEL=="ipmi-kcs4", SYMLINK+="ipmi_kcs4" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs_%.bbappend new file mode 100644 index 000000000..fd1677543 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs_%.bbappend @@ -0,0 +1,21 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +#SYSTEMD_SUBSTITUTIONS_remove = "KCS_DEVICE:${KCS_DEVICE}:${DBUS_SERVICE_${PN}}" + +# Default kcs device is ipmi-kcs3; this is SMS. +# Add SMM kcs device instance + +# Replace the '-' to '_', since Dbus object/interface names do not allow '-'. +KCS_DEVICE = "ipmi_kcs3" +SMM_DEVICE = "ipmi_kcs4" +SYSTEMD_SERVICE:${PN}:append = " ${PN}@${SMM_DEVICE}.service " + +SRC_URI = "git://github.com/openbmc/kcsbridge.git" +SRCREV = "7580a8e60d868b5bcb1a8f8d276374afe7c0983a" + +SRC_URI += "file://99-ipmi-kcs.rules" + +do_install:append() { + install -d ${D}${base_libdir}/udev/rules.d + install -m 0644 ${WORKDIR}/99-ipmi-kcs.rules ${D}${base_libdir}/udev/rules.d/ +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/00010-Change-Authentication-Parameter.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/00010-Change-Authentication-Parameter.patch new file mode 100644 index 000000000..867b3aba6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/00010-Change-Authentication-Parameter.patch @@ -0,0 +1,40 @@ +From 0fd38eb0a155cb11ff5a5452087f68c46d12111b Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@intel.com> +Date: Thu, 28 Mar 2019 18:10:40 +0800 +Subject: [PATCH] Change Authentication Parameter + +Seprate D-bus interface Authentication to forceAuthentication, +forceEncryption, Privilege according to the related change in +sol-dbus-interface. + +Tested By: +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x02 0x03 +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x02 0xc2 +The parameters has been changed to the request data in above command. + +Signed-off-by: Cheng C Yang <cheng.c.yang@intel.com> +--- + sol/sol_manager.cpp | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/sol/sol_manager.cpp b/sol/sol_manager.cpp +index de36723..0bd837e 100644 +--- a/sol/sol_manager.cpp ++++ b/sol/sol_manager.cpp +@@ -195,8 +195,12 @@ void Manager::updateSOLParameter() + + enable = std::get<bool>(properties["Enable"]); + ++ forceEncrypt = std::get<bool>(properties["ForceEncryption"]); ++ ++ forceAuth = std::get<bool>(properties["ForceAuthentication"]); ++ + solMinPrivilege = static_cast<session::Privilege>( +- std::get<uint8_t>(properties["Authentication"])); ++ std::get<uint8_t>(properties["Privilege"])); + + accumulateInterval = + std::get<uint8_t>((properties["AccumulateIntervalMS"])) * +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch new file mode 100644 index 000000000..0ad625a1f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch @@ -0,0 +1,39 @@ +From 6fc55bb689272d34ff6616cdd4b24367ea39c749 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Mon, 2 Jul 2018 15:51:52 +0800 +Subject: [PATCH] Modify dbus namespace of chassis control for guid.cpp +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Switch chassis control service namespace for guid.cpp from “org” to “xyz”, +to compatible with new intel-chassis services + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + command/guid.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +Index: phosphor-net-ipmid.clean/command/guid.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/command/guid.cpp ++++ phosphor-net-ipmid.clean/command/guid.cpp +@@ -21,7 +21,8 @@ namespace command + + std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr); + +-static constexpr auto guidObjPath = "/org/openbmc/control/chassis0"; ++static constexpr auto guidObjPath = ++ "/xyz/openbmc_project/Chassis/Control/Chassis0"; + static constexpr auto propInterface = "org.freedesktop.DBus.Properties"; + + Guid getSystemGUID() +@@ -31,7 +32,7 @@ Guid getSystemGUID() + Guid guid = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}; + +- constexpr auto chassisIntf = "org.openbmc.control.Chassis"; ++ constexpr auto chassisIntf = "xyz.openbmc_project.Chassis.Control.Chassis"; + + sd_bus_message* reply = nullptr; + sd_bus_error error = SD_BUS_ERROR_NULL; diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0011-Remove-Get-SOL-Config-Command-from-Netipmid.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0011-Remove-Get-SOL-Config-Command-from-Netipmid.patch new file mode 100644 index 000000000..7b690998f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0011-Remove-Get-SOL-Config-Command-from-Netipmid.patch @@ -0,0 +1,331 @@ +From adabdfa46aa0db56f40030c7077f991ba1987b04 Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@intel.com> +Date: Tue, 30 Apr 2019 05:35:31 +0800 +Subject: [PATCH] Remove Get SOL Config Command from Netipmid + +Since Get SOL Config Parameter command already exists in host-ipmid, and +can be shared to net channel, remove this command from net-ipmid. + +Tested: +Run ipmitool -I lanplus -H xxx -U root -P 0penBmc sol info, the command +returns the same result as ipmitool sol info as below. +Info: SOL parameter 'Nonvolatile Bitrate (5)' not supported +Info: SOL parameter 'Volatile Bitrate (6)' not supported +Info: SOL parameter 'Payload Channel (7)' not supported - defaulting to 0x0e +Set in progress : set-complete +Enabled : true +Force Encryption : true +Force Authentication : true +Privilege Level : USER +Character Accumulate Level (ms) : 100 +Character Send Threshold : 1 +Retry Count : 3 +Retry Interval (ms) : 100 +Volatile Bit Rate (kbps) : IPMI-Over-Serial-Setting +Non-Volatile Bit Rate (kbps) : IPMI-Over-Serial-Setting +Payload Channel : 14 (0x0e) +Payload Port : 623 + +Signed-off-by: Cheng C Yang <cheng.c.yang@intel.com> +--- + command/sol_cmds.cpp | 86 ---------------------- + command/sol_cmds.hpp | 168 ------------------------------------------- + sol_module.cpp | 6 -- + 3 files changed, 260 deletions(-) + +diff --git a/command/sol_cmds.cpp b/command/sol_cmds.cpp +index 81dfc993236c..be2cc81fc9cc 100644 +--- a/command/sol_cmds.cpp ++++ b/command/sol_cmds.cpp +@@ -69,92 +69,6 @@ void activating(uint8_t payloadInstance, uint32_t sessionID) + outPayload); + } + +-std::vector<uint8_t> getConfParams(const std::vector<uint8_t>& inPayload, +- std::shared_ptr<message::Handler>& handler) +-{ +- std::vector<uint8_t> outPayload(sizeof(GetConfParamsResponse)); +- auto request = +- reinterpret_cast<const GetConfParamsRequest*>(inPayload.data()); +- auto response = reinterpret_cast<GetConfParamsResponse*>(outPayload.data()); +- response->completionCode = IPMI_CC_OK; +- response->paramRev = parameterRevision; +- +- if (request->getParamRev) +- { +- return outPayload; +- } +- +- switch (static_cast<Parameter>(request->paramSelector)) +- { +- case Parameter::PROGRESS: +- { +- outPayload.push_back(sol::Manager::get().progress); +- break; +- } +- case Parameter::ENABLE: +- { +- outPayload.push_back(sol::Manager::get().enable); +- break; +- } +- case Parameter::AUTHENTICATION: +- { +- Auth value{0}; +- +- value.encrypt = sol::Manager::get().forceEncrypt; +- value.auth = sol::Manager::get().forceAuth; +- value.privilege = +- static_cast<uint8_t>(sol::Manager::get().solMinPrivilege); +- auto buffer = reinterpret_cast<const uint8_t*>(&value); +- +- std::copy_n(buffer, sizeof(value), std::back_inserter(outPayload)); +- break; +- } +- case Parameter::ACCUMULATE: +- { +- Accumulate value{0}; +- +- value.interval = sol::Manager::get().accumulateInterval.count() / +- sol::accIntervalFactor; +- value.threshold = sol::Manager::get().sendThreshold; +- auto buffer = reinterpret_cast<const uint8_t*>(&value); +- +- std::copy_n(buffer, sizeof(value), std::back_inserter(outPayload)); +- break; +- } +- case Parameter::RETRY: +- { +- Retry value{0}; +- +- value.count = sol::Manager::get().retryCount; +- value.interval = sol::Manager::get().retryInterval.count() / +- sol::retryIntervalFactor; +- auto buffer = reinterpret_cast<const uint8_t*>(&value); +- +- std::copy_n(buffer, sizeof(value), std::back_inserter(outPayload)); +- break; +- } +- case Parameter::PORT: +- { +- auto port = endian::to_ipmi<uint16_t>(IPMI_STD_PORT); +- auto buffer = reinterpret_cast<const uint8_t*>(&port); +- +- std::copy_n(buffer, sizeof(port), std::back_inserter(outPayload)); +- break; +- } +- case Parameter::CHANNEL: +- { +- outPayload.push_back(sol::Manager::get().channel); +- break; +- } +- case Parameter::NVBITRATE: +- case Parameter::VBITRATE: +- default: +- response->completionCode = ipmiCCParamNotSupported; +- } +- +- return outPayload; +-} +- + } // namespace command + + } // namespace sol +diff --git a/command/sol_cmds.hpp b/command/sol_cmds.hpp +index 3e05e0fc035f..9aedfddf0d39 100644 +--- a/command/sol_cmds.hpp ++++ b/command/sol_cmds.hpp +@@ -62,174 +62,6 @@ struct ActivatingRequest + */ + void activating(uint8_t payloadInstance, uint32_t sessionID); + +-/** @enum Parameter +- * +- * SOL parameters are volatile, they are initialized by the SOL manager. +- * They can be read using Get SOL configuration parameters command and updated +- * using Set SOL configuration parameters command. +- */ +-enum class Parameter +-{ +- PROGRESS, //!< Set In Progress. +- ENABLE, //!< SOL Enable. +- AUTHENTICATION, //!< SOL Authentication. +- ACCUMULATE, //!< Character Accumulate Interval & Send Threshold. +- RETRY, //!< SOL Retry. +- NVBITRATE, //!< SOL non-volatile bit rate. +- VBITRATE, //!< SOL volatile bit rate. +- CHANNEL, //!< SOL payload channel. +- PORT, //!< SOL payload port. +-}; +- +-constexpr uint8_t progressMask = 0x03; +-constexpr uint8_t enableMask = 0x01; +- +-/** @struct Auth +- * +- * SOL authentication parameter. +- */ +-struct Auth +-{ +-#if BYTE_ORDER == LITTLE_ENDIAN +- uint8_t privilege : 4; //!< SOL privilege level. +- uint8_t reserved : 2; //!< Reserved. +- uint8_t auth : 1; //!< Force SOL payload Authentication. +- uint8_t encrypt : 1; //!< Force SOL payload encryption. +-#endif +- +-#if BYTE_ORDER == BIG_ENDIAN +- uint8_t encrypt : 1; //!< Force SOL payload encryption. +- uint8_t auth : 1; //!< Force SOL payload Authentication. +- uint8_t reserved : 2; //!< Reserved. +- uint8_t privilege : 4; //!< SOL privilege level. +-#endif +-} __attribute__((packed)); +- +-/** @struct Accumulate +- * +- * Character accumulate interval & Character send threshold. +- */ +-struct Accumulate +-{ +- uint8_t interval; //!< Character accumulate interval. +- uint8_t threshold; //!< Character send threshold. +-} __attribute__((packed)); +- +-constexpr uint8_t retryCountMask = 0x07; +- +-/** @struct Retry +- * +- * SOL retry count and interval. +- */ +-struct Retry +-{ +-#if BYTE_ORDER == LITTLE_ENDIAN +- uint8_t count : 3; //!< SOL retry count. +- uint8_t reserved : 5; //!< Reserved. +-#endif +- +-#if BYTE_ORDER == BIG_ENDIAN +- uint8_t reserved : 5; //!< Reserved. +- uint8_t count : 3; //!< SOL retry count. +-#endif +- +- uint8_t interval; //!< SOL retry interval. +-} __attribute__((packed)); +- +-constexpr uint8_t ipmiCCParamNotSupported = 0x80; +-constexpr uint8_t ipmiCCInvalidSetInProgress = 0x81; +-constexpr uint8_t ipmiCCWriteReadParameter = 0x82; +-constexpr uint8_t ipmiCCReadWriteParameter = 0x83; +-constexpr uint8_t parameterRevision = 0x11; +- +-/** @struct SetConfParamsRequest +- * +- * IPMI payload for Set SOL configuration parameters command request. +- */ +-struct SetConfParamsRequest +-{ +-#if BYTE_ORDER == LITTLE_ENDIAN +- uint8_t channelNumber : 4; //!< Channel number. +- uint8_t reserved : 4; //!< Reserved. +-#endif +- +-#if BYTE_ORDER == BIG_ENDIAN +- uint8_t reserved : 4; //!< Reserved. +- uint8_t channelNumber : 4; //!< Channel number. +-#endif +- +- uint8_t paramSelector; //!< Parameter selector. +- union +- { +- uint8_t value; //!< Represents one byte SOL parameters. +- struct Accumulate acc; //!< Character accumulate values. +- struct Retry retry; //!< Retry values. +- struct Auth auth; //!< Authentication parameters. +- }; +-} __attribute__((packed)); +- +-/** @struct SetConfParamsResponse +- * +- * IPMI payload for Set SOL configuration parameters command response. +- */ +-struct SetConfParamsResponse +-{ +- uint8_t completionCode; //!< Completion code. +-} __attribute__((packed)); +- +-/** @brief Set SOL configuration parameters command. +- * +- * @param[in] inPayload - Request data for the command. +- * @param[in] handler - Reference to the message handler. +- * +- * @return Response data for the command. +- */ +-std::vector<uint8_t> setConfParams(const std::vector<uint8_t>& inPayload, +- std::shared_ptr<message::Handler>& handler); +- +-/** @struct GetConfParamsRequest +- * +- * IPMI payload for Get SOL configuration parameters command request. +- */ +-struct GetConfParamsRequest +-{ +-#if BYTE_ORDER == LITTLE_ENDIAN +- uint8_t channelNum : 4; //!< Channel number. +- uint8_t reserved : 3; //!< Reserved. +- uint8_t getParamRev : 1; //!< Get parameter or Get parameter revision +-#endif +- +-#if BYTE_ORDER == BIG_ENDIAN +- uint8_t getParamRev : 1; //!< Get parameter or Get parameter revision +- uint8_t reserved : 3; //!< Reserved. +- uint8_t channelNum : 4; //!< Channel number. +-#endif +- +- uint8_t paramSelector; //!< Parameter selector. +- uint8_t setSelector; //!< Set selector. +- uint8_t blockSelector; //!< Block selector. +-} __attribute__((packed)); +- +-/** @struct GetConfParamsResponse +- * +- * IPMI payload for Get SOL configuration parameters command response. +- */ +-struct GetConfParamsResponse +-{ +- uint8_t completionCode; //!< Completion code. +- uint8_t paramRev; //!< Parameter revision. +-} __attribute__((packed)); +- +-/** @brief Get SOL configuration parameters command. +- * +- * @param[in] inPayload - Request data for the command. +- * @param[in] handler - Reference to the message handler. +- * +- * @return Response data for the command. +- */ +-std::vector<uint8_t> getConfParams(const std::vector<uint8_t>& inPayload, +- std::shared_ptr<message::Handler>& handler); +- + } // namespace command + + } // namespace sol +diff --git a/sol_module.cpp b/sol_module.cpp +index d9a9a7c9551f..21196d8a2cbf 100644 +--- a/sol_module.cpp ++++ b/sol_module.cpp +@@ -41,12 +41,6 @@ void registerCommands() + &getPayloadInfo, + session::Privilege::USER, + false}, +- // Get SOL Configuration Parameters +- {{(static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | +- static_cast<uint16_t>(::command::NetFns::TRANSPORT) | 0x22}, +- &getConfParams, +- session::Privilege::USER, +- false}, + }; + + for (const auto& iter : commands) +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0012-rakp12-Add-username-to-SessionInfo-interface.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0012-rakp12-Add-username-to-SessionInfo-interface.patch new file mode 100644 index 000000000..89a111d06 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0012-rakp12-Add-username-to-SessionInfo-interface.patch @@ -0,0 +1,49 @@ +From 20bf13de482b02a4a467f44070f7ff184c340dd2 Mon Sep 17 00:00:00 2001 +From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +Date: Tue, 1 Jun 2021 12:22:19 +0000 +Subject: [PATCH] rakp12: Add username to SessionInfo interface + +Add username to SessionInfo interface to get username info on individual +IPMI session Id's on Redfish + +Tested: +1. Activate SOL session +Command: ipmitool -I lanplus -U root -P 0penBmc -H <BMC_IP> -C 17 + sol activate +Response: // Success +2. Verified on SessionInfo D-bus interface. +busctl introspect xyz.openbmc_project.Ipmi.Channel.eth0 + /xyz/openbmc_project/ipmi/session/eth0/<session_id> +NAME TYPE SIGNATURE RESULT/VALUE FLAGS +...... +xyz.openbmc_project.Ipmi.SessionInfo interface - - - +.ChannelNum property y 3 emits-change writable +.CurrentPrivilege property y 4 emits-change writable +.RemoteIPAddr property u 22253066 emits-change writable +.RemoteMACAddress property ay 0 emits-change writable +.RemotePort property q 41096 emits-change writable +.SessionHandle property y 129 emits-change writable +.State property y 2 emits-change writable +.UserID property y 1 emits-change writable +.Username property s "root" emits-change writable + +Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +--- + command/rakp12.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/command/rakp12.cpp b/command/rakp12.cpp +index 099c5dc..98b6891 100644 +--- a/command/rakp12.cpp ++++ b/command/rakp12.cpp +@@ -227,6 +227,7 @@ std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload, + } + session->channelNum(chNum); + session->userID(userId); ++ session->username(userName); + // minimum privilege of Channel / User / session::privilege::USER + // has to be used as session current privilege level + uint8_t minPriv = 0; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/10-nice-rules.conf b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/10-nice-rules.conf new file mode 100644 index 000000000..d2fb5ba04 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/10-nice-rules.conf @@ -0,0 +1,2 @@ +[Service] +Nice=-18 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net_%.bbappend new file mode 100644 index 000000000..a0b507cb8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net_%.bbappend @@ -0,0 +1,31 @@ +inherit useradd + +# TODO: This should be removed, once up-stream bump up +# issue is resolved +SRC_URI += "git://github.com/openbmc/phosphor-net-ipmid" +SRCREV = "5819666c23ee1d01a54fc5fb2c068bb1da1f29c7" + +USERADD_PACKAGES = "${PN}" +# add a group called ipmi +GROUPADD_PARAM:${PN} = "ipmi " + +# Default rmcpp iface is eth0; channel 1 +# Add channel 2 instance (eth1) +RMCPP_EXTRA = "eth1" +SYSTEMD_SERVICE:${PN} += " \ + ${PN}@${RMCPP_EXTRA}.service \ + ${PN}@${RMCPP_EXTRA}.socket \ + " + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI += " file://10-nice-rules.conf \ + file://0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch \ + file://0011-Remove-Get-SOL-Config-Command-from-Netipmid.patch \ + file://0012-rakp12-Add-username-to-SessionInfo-interface.patch \ + " + +do_install:append() { + mkdir -p ${D}${sysconfdir}/systemd/system/phosphor-ipmi-net@.service.d/ + install -m 0644 ${WORKDIR}/10-nice-rules.conf ${D}${sysconfdir}/systemd/system/phosphor-ipmi-net@.service.d/ +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%.bbappend new file mode 100644 index 000000000..4fc41d058 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%/config.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%/config.yaml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%/config.yaml diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-node-manager-proxy_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-node-manager-proxy_git.bb new file mode 100644 index 000000000..14093cf3e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-node-manager-proxy_git.bb @@ -0,0 +1,19 @@ +SUMMARY = "Node Manager Proxy" +DESCRIPTION = "The Node Manager Proxy provides a simple interface for communicating \ +with Management Engine via IPMB" + +SRC_URI = "git://github.com/Intel-BMC/node-manager;protocol=ssh" +SRCREV = "9aca80fa2a405938de99aae777e6cfcf08525563" +PV = "0.1+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SYSTEMD_SERVICE:${PN} = "node-manager-proxy.service" + +DEPENDS = "sdbusplus \ + phosphor-logging \ + boost" + +S = "${WORKDIR}/git" +inherit cmake systemd diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/id-led-off/id-led-off.service b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/id-led-off/id-led-off.service new file mode 100644 index 000000000..51e59c614 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/id-led-off/id-led-off.service @@ -0,0 +1,11 @@ +[Unit] +Description=turn off the ID LED when BMC is ready +Wants=multi-user.target +After=multi-user.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/id-led-off.sh + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/id-led-off/id-led-off.sh b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/id-led-off/id-led-off.sh new file mode 100755 index 000000000..b609fc0ea --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/id-led-off/id-led-off.sh @@ -0,0 +1,12 @@ +#!/bin/sh +busctl set-property "xyz.openbmc_project.LED.GroupManager" \ +"/xyz/openbmc_project/led/groups/enclosure_identify" \ +"xyz.openbmc_project.Led.Group" "Asserted" b false + +busctl set-property "xyz.openbmc_project.LED.GroupManager" \ +"/xyz/openbmc_project/led/groups/enclosure_identify_blink" \ +"xyz.openbmc_project.Led.Group" "Asserted" b false + +busctl set-property "xyz.openbmc_project.LED.Controller.identify" \ +"/xyz/openbmc_project/led/physical/identify" \ +"xyz.openbmc_project.Led.Physical" "State" s "xyz.openbmc_project.Led.Physical.Action.Off" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/id-led-off_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/id-led-off_git.bb new file mode 100644 index 000000000..21557d96b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/id-led-off_git.bb @@ -0,0 +1,24 @@ +SUMMARY = "Turn off the ID LED" +DESCRIPTION = "Script to turn off the ID LED after BMC is ready" + +S = "${WORKDIR}" +SRC_URI = "file://id-led-off.sh \ + file://id-led-off.service \ + " + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" +RDEPENDS:${PN} += "bash" + +inherit systemd + +FILES:${PN} += "${systemd_system_unitdir}/id-led-off.service" + +do_install() { + install -d ${D}${systemd_system_unitdir} + install -m 0644 ${WORKDIR}/id-led-off.service ${D}${systemd_system_unitdir} + install -d ${D}${bindir} + install -m 0755 ${S}/id-led-off.sh ${D}/${bindir}/id-led-off.sh +} + +SYSTEMD_SERVICE:${PN} += " id-led-off.service" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config-native.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config-native.bb new file mode 100644 index 000000000..47c66ccb8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config-native.bb @@ -0,0 +1,21 @@ +SUMMARY = "Phosphor LED Group Management for Intel" +PR = "r1" + +inherit obmc-phosphor-utils +inherit native + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +PROVIDES += "virtual/phosphor-led-manager-config-native" + +SRC_URI += "file://led.yaml" +S = "${WORKDIR}" + +# Overwrite the example led layout yaml file prior +# to building the phosphor-led-manager package +do_install() { + SRC=${S} + DEST=${D}${datadir}/phosphor-led-manager + install -D ${SRC}/led.yaml ${DEST}/led.yaml +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config/led.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config/led.yaml new file mode 100644 index 000000000..1605b8e6b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config/led.yaml @@ -0,0 +1,80 @@ +bmc_booted: + +power_on: + +status_ok: + status_green: + Action: 'On' + status_amber: + Action: 'Off' + +status_degraded: + status_green: + Action: 'Blink' + DutyOn: 50 + Period: 1000 + status_amber: + Action: 'Off' + +status_non_critical: + status_green: + Action: 'Off' + status_amber: + Action: 'Blink' + DutyOn: 50 + Period: 1000 + +status_critical: + status_green: + Action: 'Off' + status_amber: + Action: 'On' + +enclosure_identify: + identify: + Action: 'On' + +enclosure_identify_blink: + identify: + Action: 'Blink' + +cpu0_fault: + cpu0fault: + Action: 'On' + +cpu1_fault: + cpu1fault: + Action: 'On' + +fan1_fault: + fan1_fault: + Action: 'On' + +fan2_fault: + fan2_fault: + Action: 'On' + +fan3_fault: + fan3_fault: + Action: 'On' + +fan4_fault: + fan4_fault: + Action: 'On' + +fan5_fault: + fan5_fault: + Action: 'On' + +fan6_fault: + fan6_fault: + Action: 'On' + +fan7_fault: + fan7_fault: + Action: 'On' + +fan8_fault: + fan8_fault: + Action: 'On' + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/phosphor-led-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/phosphor-led-manager_%.bbappend new file mode 100644 index 000000000..132fe2337 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/phosphor-led-manager_%.bbappend @@ -0,0 +1,13 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +RDEPENDS:${PN}:remove = "clear-once" + +do_compile:prepend(){ + install -m 0644 ${STAGING_DATADIR_NATIVE}/${PN}/led.yaml ${S} +} + +do_install:append(){ + rm -f ${S}/led.yaml +} + + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl.bb new file mode 100644 index 000000000..2468a487d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl.bb @@ -0,0 +1,27 @@ +SUMMARY = "Multi-node Non-legacy" +DESCRIPTION = "New systemd target for non-legacy nodes on multi-node platform" + +inherit systemd + +SYSTEMD_SERVICE:${PN} = "multi-node-nl.target" +SYSTEMD_SERVICE:${PN} += "nonLegacyNode.service" + +S = "${WORKDIR}" +SRC_URI = "file://multi-node-nl.target \ + file://nonLegacyNode.service \ + file://nonLegacyNode.sh \ + " + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +RDEPENDS:${PN} = "bash" + +do_install:append() { + install -d ${D}${bindir} + install -m 0755 ${S}/nonLegacyNode.sh ${D}/${bindir}/nonLegacyNode.sh + + install -d ${D}${base_libdir}/systemd/system + install -m 0644 ${S}/multi-node-nl.target ${D}${base_libdir}/systemd/system + install -m 0644 ${S}/nonLegacyNode.service ${D}${base_libdir}/systemd/system +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl/multi-node-nl.target b/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl/multi-node-nl.target new file mode 100644 index 000000000..32b50532f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl/multi-node-nl.target @@ -0,0 +1,4 @@ +[Unit] +Description=Target for non-legacy node in multi-node system +Documentation=man:systemd.special(7) + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl/nonLegacyNode.service b/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl/nonLegacyNode.service new file mode 100644 index 000000000..8e3d07ba4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl/nonLegacyNode.service @@ -0,0 +1,9 @@ +[Unit] +Description=Non Legacy node + +[Service] +ExecStart=/usr/bin/nonLegacyNode.sh +Type=exec + +[Install] +WantedBy=multi-node-nl.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl/nonLegacyNode.sh b/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl/nonLegacyNode.sh new file mode 100755 index 000000000..28a6bbe2a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/multi-node-nl/multi-node-nl/nonLegacyNode.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +PWM_FILE="/sys/class/hwmon/hwmon0/pwm" +FAN_SPEED=$((255 * 80 / 100)) + +set_fan_speed() { + local idx=0 + for ((idx=1; idx<=8; idx++)) + do + if [ -f $PWM_FILE$idx ]; then + echo $FAN_SPEED > $PWM_FILE$idx + fi + done +} + +$(set_fan_speed) + +#Stop power control service in NL mode +systemctl stop xyz.openbmc_project.Chassis.Control.Power.service + +export TERM=xterm +# Autologin root user to serial console (ttyS4) on boot +exec /sbin/agetty -a root -J -8 -L ttyS4 115200 $TERM diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/network/phosphor-snmp_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/network/phosphor-snmp_%.bbappend new file mode 100644 index 000000000..6d9294635 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/network/phosphor-snmp_%.bbappend @@ -0,0 +1,4 @@ +FILESEXTRAPATHS:append := ":${THISDIR}/${PN}" + +# SRC_URI = "git://github.com/openbmc/phosphor-snmp" +SRCREV = "d560529eb7e22c0b78fb0def20f57c6f35be9dfe" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/peci/peci-pcie_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/peci/peci-pcie_%.bbappend new file mode 100644 index 000000000..e7c5d1af3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/peci/peci-pcie_%.bbappend @@ -0,0 +1,5 @@ +SRC_URI = "git://github.com/openbmc/peci-pcie" + +SRCREV = "de624395a587be555463a14a3db90500b4e0521c" + +EXTRA_OECMAKE += "-DWAIT_FOR_OS_STANDBY=1 -DUSE_RDENDPOINTCFG=1" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/phosphor-u-boot-mgr/phosphor-u-boot-mgr_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/phosphor-u-boot-mgr/phosphor-u-boot-mgr_git.bb new file mode 100644 index 000000000..5a6191fcd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/phosphor-u-boot-mgr/phosphor-u-boot-mgr_git.bb @@ -0,0 +1,18 @@ +SUMMARY = "Phosphor U-Boot environment manager" +DESCRIPTION = "Daemon to read or write U-Boot environment variables" + +PV = "1.0+git${SRCPV}" + +S = "${WORKDIR}/git" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327" + +SRC_URI = "git://github.com/openbmc/phosphor-u-boot-env-mgr.git;protocol=ssh" + +SRCREV = "1979d3b31a96e9359402ac4d7867ec5dddbece7e" + +inherit cmake systemd +SYSTEMD_SERVICE:${PN} = "xyz.openbmc_project.U_Boot.Environment.Manager.service" + +DEPENDS = "boost sdbusplus phosphor-logging" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libmctp-intel_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libmctp-intel_git.bb new file mode 100644 index 000000000..592d6ae0c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libmctp-intel_git.bb @@ -0,0 +1,16 @@ +SUMMARY = "libmctp:intel" +DESCRIPTION = "Implementation of MCTP(DMTF DSP0236)" + +SRC_URI = "git://github.com/Intel-BMC/libmctp.git;protocol=ssh" +SRCREV = "d530c2271e1f9ff5d76a170c0abd64bd03ef40fd" + +S = "${WORKDIR}/git" + +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0 | GPLv2" +LIC_FILES_CHKSUM = "file://LICENSE;md5=0d30807bb7a4f16d36e96b78f9ed8fae" + +inherit cmake + +DEPENDS += "i2c-tools" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libpldm-intel_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libpldm-intel_git.bb new file mode 100644 index 000000000..162b4ed74 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libpldm-intel_git.bb @@ -0,0 +1,18 @@ +SUMMARY = "libpldm_intel" +DESCRIPTION = "Provides encode/decode APIs for PLDM specifications" + +SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" +SRCREV = "94437a678a1d23b22dc179b5cb7b165e52a429c0" + +S = "${WORKDIR}/git/libpldm_intel" + +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327" + +inherit cmake + +DEPENDS += " \ + gtest \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-emulator.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-emulator.bb new file mode 100644 index 000000000..fda4d6b79 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-emulator.bb @@ -0,0 +1,30 @@ +SUMMARY = "MCTP Daemon" +DESCRIPTION = "Implementation of MCTP (DTMF DSP0236)" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=bcd9ada3a943f58551867d72893cc9ab" + +SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" +SRCREV = "94437a678a1d23b22dc179b5cb7b165e52a429c0" + +S = "${WORKDIR}/git/mctp_emulator" + +PV = "1.0+git${SRCPV}" + +inherit cmake systemd + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +DEPENDS += " \ + libmctp-intel \ + systemd \ + sdbusplus \ + phosphor-logging \ + boost \ + i2c-tools \ + cli11 \ + nlohmann-json \ + gtest \ + " + +SYSTEMD_SERVICE:${PN} = "xyz.openbmc_project.mctp-emulator.service" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-wrapper.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-wrapper.bb new file mode 100644 index 000000000..05e64ed46 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-wrapper.bb @@ -0,0 +1,28 @@ +SUMMARY = "MCTP Wrapper Library" +DESCRIPTION = "Implementation of MCTP Wrapper Library" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=bcd9ada3a943f58551867d72893cc9ab" + +SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" +SRCREV = "94437a678a1d23b22dc179b5cb7b165e52a429c0" + +S = "${WORKDIR}/git/mctp_wrapper" + +PV = "1.0+git${SRCPV}" + +inherit cmake systemd + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +DEPENDS += " \ + libmctp-intel \ + systemd \ + sdbusplus \ + phosphor-logging \ + gtest \ + boost \ + phosphor-dbus-interfaces \ + " + +EXTRA_OECMAKE += "-DYOCTO_DEPENDENCIES=ON" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpd.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpd.bb new file mode 100644 index 000000000..8b088be23 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpd.bb @@ -0,0 +1,34 @@ +SUMMARY = "MCTP Daemon" +DESCRIPTION = "Implementation of MCTP (DTMF DSP0236)" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${PN}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" +SRCREV = "94437a678a1d23b22dc179b5cb7b165e52a429c0" + +S = "${WORKDIR}/git" + +PV = "1.0+git${SRCPV}" + +OECMAKE_SOURCEPATH = "${S}/${PN}" + +inherit cmake systemd + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +DEPENDS += " \ + libmctp-intel \ + systemd \ + sdbusplus \ + phosphor-logging \ + boost \ + i2c-tools \ + cli11 \ + nlohmann-json \ + gtest \ + phosphor-dbus-interfaces \ + udev \ + " +FILES:${PN} += "${systemd_system_unitdir}/xyz.openbmc_project.mctpd@.service" +FILES:${PN} += "/usr/share/mctp/mctp_config.json" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpwplus.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpwplus.bb new file mode 100644 index 000000000..b1c40ab61 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpwplus.bb @@ -0,0 +1,24 @@ +SUMMARY = "MCTP Wrapper Library Plus" +DESCRIPTION = "Implementation of MCTP Wrapper Library Plus" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=615045c30a05cde5c0e924854d43c327" + +SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" +SRCREV = "94437a678a1d23b22dc179b5cb7b165e52a429c0" + +S = "${WORKDIR}/git/mctpwplus" + +PV = "1.0+git${SRCPV}" + +inherit cmake + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +DEPENDS += " \ + systemd \ + sdbusplus \ + phosphor-logging \ + cli11 \ + " +EXTRA_OECMAKE += "-DYOCTO_DEPENDENCIES=ON" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/nvmemi-daemon.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/nvmemi-daemon.bb new file mode 100644 index 000000000..84c2c8066 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/nvmemi-daemon.bb @@ -0,0 +1,17 @@ +SUMMARY = "NVMe MI Daemon" +DESCRIPTION = "Implementation of NVMe MI daemon" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327" + +SRC_URI = "git://github.com/Intel-BMC/nvme-mi.git;protocol=ssh" +SRCREV = "c3d5021fb60cd46d5c948c69f3d57ac9648b5be5" +S = "${WORKDIR}/git" +PV = "1.0+git${SRCPV}" + +inherit meson systemd + +SYSTEMD_SERVICE:${PN} += "xyz.openbmc_project.nvme-mi.service" +DEPENDS = "boost sdbusplus systemd phosphor-logging mctpwplus googletest nlohmann-json" + +EXTRA_OEMESON = "-Dyocto_dep='enabled'" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pldmd.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pldmd.bb new file mode 100644 index 000000000..16253b5d0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pldmd.bb @@ -0,0 +1,31 @@ +SUMMARY = "PLDM Requester Stack" +DESCRIPTION = "Implementation of PLDM specifications" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327" + +SRC_URI += "git://github.com/Intel-BMC/pmci.git;protocol=ssh" +SRCREV = "94437a678a1d23b22dc179b5cb7b165e52a429c0" + +S = "${WORKDIR}/git/pldmd" + +PV = "1.0+git${SRCPV}" + +inherit cmake systemd + +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +DEPENDS += " \ + libpldm-intel \ + mctp-wrapper \ + systemd \ + sdbusplus \ + phosphor-logging \ + gtest \ + boost \ + phosphor-dbus-interfaces \ + mctpwplus \ + " + +FILES:${PN} += "${systemd_system_unitdir}/xyz.openbmc_project.pldmd.service" +SYSTEMD_SERVICE:${PN} += "xyz.openbmc_project.pldmd.service" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pmci-launcher.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pmci-launcher.bb new file mode 100644 index 000000000..c640c5664 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pmci-launcher.bb @@ -0,0 +1,23 @@ +SUMMARY = "PMCI Launcher" +DESCRIPTION = "Support to launch pmci services on-demand" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" +SRCREV = "94437a678a1d23b22dc179b5cb7b165e52a429c0" + +S = "${WORKDIR}/git/pmci_launcher" + +PV = "1.0+git${SRCPV}" + +inherit cmake systemd + +DEPENDS += " \ + systemd \ + sdbusplus \ + phosphor-logging \ + boost \ + " +FILES:${PN} += "${systemd_system_unitdir}/xyz.openbmc_project.pmci-launcher.service" +SYSTEMD_SERVICE:${PN} += "xyz.openbmc_project.pmci-launcher.service" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts.bbappend new file mode 100644 index 000000000..76b60aa8e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI = "file://init" + +RDEPENDS:${PN} += "bash" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts/init b/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts/init new file mode 100755 index 000000000..2065f94ee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts/init @@ -0,0 +1,268 @@ +#!/bin/bash + +# Copyright 2017-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# provide a couple of places in the RO root filesystem +# that can be made RW with an overlayfs + +log() { + [ -c /dev/kmsg ] && echo "init: $*" > /dev/kmsg + echo "init: $*" +} + +# start with /proc and /tmp mounted +[ -e /proc/mounts ] || mount -t proc proc /proc +# FIXME: add size limits to /tmp +grep -q /tmp /proc/mounts || mount -t tmpfs -o rw,nosuid,nodev tmp /tmp +grep -q /sys /proc/mounts || mount -t sysfs -o rw,nosuid,nodev,noexec sys /sys + +# fix up /srv to be RW +mkdir -p /tmp/srv +mount --bind /tmp/srv /srv + +if grep -q debug-init /proc/cmdline; then + exec > /tmp/init.log 2>&1 + set -x + env +else + # Suppress any stray output but we want to see any errors + exec >/dev/null 2>/dev/kmsg +fi + +# list of things that need to be rw at boot +NV_OVERLAYS="/etc /var /home" + +# place to mount the overlay backing store +OVERLAY_MNT=/tmp/.overlay +# OVERLAY_SIZE=16384 +# place to mount NV +RWFS_MNT=/tmp/.rwfs +# NV overlay storage +OVERLAY_SYNC=${RWFS_MNT}/.overlay + +if grep -q "$RWFS_MNT" /proc/mounts; then + # quit - we have already run + exit 0 +fi +mkdir -p "$OVERLAY_MNT" +# TODO: remount the overlay with a size limit? +# mount -t tmpfs -o rw,size=${OVERLAY_SIZE} oltmp ${OVERLAY_MNT} + +mtd_by_name() { + local name="$1" + echo "/dev/$(grep "$name" /proc/mtd | cut -d : -f 1)" +} + +mtdnum_by_name() { + local name="$1" + grep "$name" /proc/mtd | cut -c 4 +} + +NV_MTD=rwfs + +nvrw() { + local p="$1" + # Clear the work dir doing overlay mount + rm -rf "${OVERLAY_MNT}${p}.work" + mkdir -p "${OVERLAY_MNT}${p}" "${OVERLAY_MNT}${p}.work" + local mname + mname=$(echo "ol${p}" | sed 's,/,,g') + local opts="lowerdir=${p},upperdir=${OVERLAY_MNT}${p},workdir=${OVERLAY_MNT}${p}.work,sync" + mount -t overlay -o "$opts" "$mname" "$p" +} + +targeted_clean() { + log "restore-defaults: targeted_clean" + # Do not delete FRU info, ssh/ssl certs, or machine-id + ( + cd "${OVERLAY_SYNC}/etc" || exit + find . ! -regex '.*/\(ssl\|dropbear\|machine-id\|fru\).*' -a ! -path '.' \ + -exec rm -rf {} + + ) + # nothing should be in the workdir, but clear it just in case + rm -rf "${OVERLAY_SYNC}/etc.work" + + # clean everything out of /home + rm -rf "${OVERLAY_SYNC}/home" "${OVERLAY_SYNC}/home.work" + + # clean everything out of /var + rm -rf "${OVERLAY_SYNC}/var" "${OVERLAY_SYNC}/var.work" + + echo "Files remaining: $(find $OVERLAY_SYNC/)" + sync +} + +full_clean() { + log "restore-defaults: full_clean" + local OVL='' + for OVL in $NV_OVERLAYS; do + rm -rf "${OVERLAY_SYNC}${OVL}" "${OVERLAY_SYNC}${OVL}.work" + done + sync +} + +jffs2_mount() { + mtd_name=$1 + mnt=$2 + mount -t jffs2 -o sync,ro mtd:"$mtd_name" "$mnt" +} + +reformat_jffs2_partition() { + local mtd_name="$1" + local mnt="$2" + # unmount the partition to reformat it + umount -f "$mnt" + flash_erase "$(mtd_by_name "$mtd_name")" 0 0 + # remount the JFFS2 + if ! jffs2_mount "$mtd_name" "$mnt"; then + log "Failed to mount reformatted NV volume; system unstable" + fi +} + +clear_ubenv() { + log "Clearing U-Boot environment" + flash_erase "$(mtd_by_name u-boot-env)" 0 0 +} + +# mount NV filesystem +mkdir -p "$RWFS_MNT" +if ! jffs2_mount "$NV_MTD" "$RWFS_MNT"; then + log "Failed to mount NV volume; attempting recovery" + reformat_jffs2_partition $NV_MTD $RWFS_MNT +fi + +# check for full factory reset: if so, format $NV_MTD_DEV +RESTORE_FLAG=$RWFS_MNT/.restore_op +if [ -f "$RESTORE_FLAG" ]; then + mount -o remount,rw "$RWFS_MNT" + restore_op=$(cat $RESTORE_FLAG) # read from NV + modified_on=$(stat -c %y $RESTORE_FLAG) # get last modified time + log "restore_op: $restore_op modified on: $modified_on" + # To rule out stale file mounted, Write unique, unused value 0x10 + # (last 2-bits are b00) before removing the file. + echo 16 > $RESTORE_FLAG + # set default value 0 if RESTORE_FLAG file was empty + restore_op=${restore_op:-0} + restore_op=$((restore_op & 3)) # mask off 2 bits + if [ $restore_op -eq 1 ]; then + targeted_clean + elif [ $restore_op -eq 2 ]; then + full_clean + clear_ubenv + elif [ $restore_op -eq 3 ]; then + log "restore-defaults: reformat" + reformat_jffs2_partition $NV_MTD $RWFS_MNT + clear_ubenv + fi + rm -f $RESTORE_FLAG + mount -o remount,ro "$RWFS_MNT" +fi + +# Restore the overlay saved in the sync +rsync -a --delete "${OVERLAY_SYNC}/" "${OVERLAY_MNT}" +log "Restored overlay from sync location" + +for FS in $NV_OVERLAYS; do + nvrw "$FS" +done + +# work around bug where /etc/machine-id will be mounted with a temporary file +# if rootfs is read-only and the file is empty +MACHINE_ID=/etc/machine-id +generate_machine_id() { + systemd-machine-id-setup + cp -pf "$MACHINE_ID" "${MACHINE_ID}_bkup" +} + +if [ ! -s "$MACHINE_ID" ]; then + # work around - Bug: Overlay fs fails for machine-id due to + # origin mismatch. Clean it up, from overlay fs before re-creating + # the same. + if [ -e "$OVERLAY_MNT$MACHINE_ID" ]; then + umount "/etc" + rm -f "$OVERLAY_MNT$MACHINE_ID" + nvrw "/etc" + # Restore the machine-id from backup, else generate it. + if [ -s "${MACHINE_ID}_bkup" ]; then + cp -pf "${MACHINE_ID}_bkup" "${MACHINE_ID}" + else + generate_machine_id + fi + log "Remounted /etc for machine-id origin mismatch" + else + generate_machine_id + fi +fi + +# mount persistent NV filesystem, where immortal settings live +SOFS_MNT=/var/sofs +if ! grep -q sofs /proc/mounts; then + mkdir -p $SOFS_MNT + SOFS_MTD=sofs + + # mount a JFFS2 on the partition + if ! jffs2_mount "$SOFS_MTD" "$SOFS_MNT"; then + log "Failed to mount SOFS volume; attempting recovery" + reformat_jffs2_partition $SOFS_MTD $SOFS_MNT + fi +fi + +log "Finished mounting nv and overlays" + + +# Detect the non-legacy node in cooper city and boot in to special mode. + +readonly COOPER_CITY=40 # Board id of cooper city + +is_nl_node() { + typeset -i nid1=$(gpioget $(gpiofind "FM_NODE_ID_1")) + typeset -i nid2=$(gpioget $(gpiofind "FM_NODE_ID_2")) + echo $((nid1|nid2)) +} + +read_board_id() { + local idx=0 + local result=0 + local value=0 + for ((idx=0; idx<6; idx++)) + do + typeset -i value=$(gpioget $(gpiofind "FM_BMC_BOARD_SKU_ID${idx}_N")) + value=$((value << idx)) + result=$((result | value)) + done + echo $result +} + +pfr_write() { + [ $# -ne 2 ] && return 1 + local PFR_BUS=4 + local PFR_ADDR=0x38 + local reg=$1 + local val=$2 + i2cset -y $PFR_BUS $PFR_ADDR "$reg" "$val" >&/dev/null +} + +board_id=$(read_board_id) +if [ "$board_id" -eq $COOPER_CITY ]; then + if [ "$(is_nl_node)" -ne 0 ]; then + systemctl set-default multi-node-nl.target + PFR_BMC_CHECKPOINT_REG=0xf + PFR_BMC_CHECKPOINT_COMPLETE=0x9 + pfr_write $PFR_BMC_CHECKPOINT_REG $PFR_BMC_CHECKPOINT_COMPLETE + fi +fi + +exec /lib/systemd/systemd diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/prov-mode-mgr/prov-mode-mgr_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/prov-mode-mgr/prov-mode-mgr_git.bb new file mode 100644 index 000000000..79971942a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/prov-mode-mgr/prov-mode-mgr_git.bb @@ -0,0 +1,18 @@ +SUMMARY = "Provision mode daemon - RestrictionMode" +DESCRIPTION = "Daemon allows to configure RestrictionMode property" + +PV = "1.0+git${SRCPV}" + +S = "${WORKDIR}/git" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SRC_URI = "git://github.com/Intel-BMC/provisioning-mode-manager.git;protocol=ssh" + +SRCREV = "0aca01b4ce9b303e12ba0f757f56390da139c8bb" + +inherit cmake systemd +SYSTEMD_SERVICE:${PN} = "xyz.openbmc_project.RestrictionMode.Manager.service" + +DEPENDS = "boost sdbusplus phosphor-logging" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/security-manager/security-manager_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/security-manager/security-manager_git.bb new file mode 100644 index 000000000..783dea029 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/security-manager/security-manager_git.bb @@ -0,0 +1,23 @@ +SUMMARY = "Security Manager daemon to detect the security violation- ASD/ user management" +DESCRIPTION = "Daemon check for Remote debug enable and user account violation" + +PV = "1.0+git${SRCPV}" + +S = "${WORKDIR}/git/security-manager" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" +inherit cmake systemd + +SRC_URI = "git://github.com/Intel-BMC/provingground.git;protocol=ssh" +SRCREV = "bee56d62b209088454d166d1efae4825a2b175df" + +SYSTEMD_SERVICE:${PN} += "xyz.openbmc_project.SecurityManager.service" + +DEPENDS += " \ + systemd \ + sdbusplus \ + libgpiod \ + phosphor-logging \ + boost \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sel-logger/phosphor-sel-logger_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/sel-logger/phosphor-sel-logger_%.bbappend new file mode 100644 index 000000000..8c2eeb83f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sel-logger/phosphor-sel-logger_%.bbappend @@ -0,0 +1,4 @@ +# Enable downstream autobump +SRC_URI = "git://github.com/openbmc/phosphor-sel-logger.git" +SRCREV = "87e3fcf439f2b943272365e1d294984f39bb52b8" + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test/xyz.openbmc_project.selftest.service b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test/xyz.openbmc_project.selftest.service new file mode 100644 index 000000000..b8c3554ae --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test/xyz.openbmc_project.selftest.service @@ -0,0 +1,10 @@ +[Unit] +Description= BMC Self-Test + +[Service] +Restart=always +ExecStart=/usr/bin/env selftest +SyslogIdentifier=selftest + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0001-Add-check-for-min-max-received-from-hwmon-files.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0001-Add-check-for-min-max-received-from-hwmon-files.patch new file mode 100644 index 000000000..33d35ec5e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0001-Add-check-for-min-max-received-from-hwmon-files.patch @@ -0,0 +1,108 @@ +From 2516d67f8bb5ecd241b8dcdec3f8c58d0e3c4744 Mon Sep 17 00:00:00 2001 +From: Wojciech Dembinski <wojciech.dembinski@intel.com> +Date: Mon, 7 Dec 2020 19:23:10 +0100 +Subject: [PATCH] Add check for min/max received from hwmon files + +When hwmon reports incorrect min/max values or CPU Sensor cannot access +readings, it shall keep the last known good readings and not update +DBus with incorrect values. +This patch adds min < max verification check for the values received +from hwmon and removes check for power on/off in the case of a read +failure. + +Tested manually on a physical platform, test cases cover incorrect +max/min values and failing access to hwmon files. +SDR over IPMI can be fully received in the case of error. + +Signed-off-by: Wojciech Dembinski <wojciech.dembinski@intel.com> +Change-Id: Ia061f849b0f434812f822ed1902c8964d4c64b45 +--- + src/CPUSensor.cpp | 50 ++++++++++++++++++++++++----------------------- + 1 file changed, 26 insertions(+), 24 deletions(-) + +diff --git a/src/CPUSensor.cpp b/src/CPUSensor.cpp +index 2356821..01f5eb6 100644 +--- a/src/CPUSensor.cpp ++++ b/src/CPUSensor.cpp +@@ -1,5 +1,5 @@ + /* +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2021 Intel Corporation + // + // Licensed under the Apache License, Version 2.0 (the "License"); + // you may not use this file except in compliance with the License. +@@ -146,19 +146,22 @@ void CPUSensor::setupRead(void) + + void CPUSensor::updateMinMaxValues(void) + { ++ double newMin = std::numeric_limits<double>::quiet_NaN(); ++ double newMax = std::numeric_limits<double>::quiet_NaN(); ++ + const boost::container::flat_map< + std::string, + std::vector<std::tuple<const char*, std::reference_wrapper<double>, +- const char*>>> +- map = { ++ const char*, std::reference_wrapper<double>>>> ++ map = { ++ { ++ "cap", + { +- "cap", +- { +- std::make_tuple("cap_max", std::ref(maxValue), "MaxValue"), +- std::make_tuple("cap_min", std::ref(minValue), "MinValue"), +- }, ++ std::make_tuple("cap_max", std::ref(maxValue), "MaxValue", std::ref(newMax)), ++ std::make_tuple("cap_min", std::ref(minValue), "MinValue", std::ref(newMin)), + }, +- }; ++ }, ++ }; + + if (auto fileParts = splitFileName(path)) + { +@@ -168,26 +171,25 @@ void CPUSensor::updateMinMaxValues(void) + { + for (const auto& vectorItem : mapIt->second) + { +- auto& [suffix, oldValue, dbusName] = vectorItem; ++ auto& [suffix, oldValue, dbusName, newValue] = vectorItem; + auto attrPath = boost::replace_all_copy(path, fileItem, suffix); +- if (auto newVal = +- readFile(attrPath, CPUSensor::sensorScaleFactor)) ++ ++ if(auto tmp = readFile(attrPath, CPUSensor::sensorScaleFactor)) + { +- updateProperty(sensorInterface, oldValue, *newVal, +- dbusName); ++ newValue.get() = *tmp; + } + else + { +- if (isPowerOn()) +- { +- updateProperty(sensorInterface, oldValue, 0, dbusName); +- } +- else +- { +- updateProperty(sensorInterface, oldValue, +- std::numeric_limits<double>::quiet_NaN(), +- dbusName); +- } ++ newValue.get() = std::numeric_limits<double>::quiet_NaN(); ++ } ++ } ++ ++ if(std::isfinite(newMin) && std::isfinite(newMax) && (newMin < newMax)) ++ { ++ for (const auto& vectorItem : mapIt->second) ++ { ++ auto& [suffix, oldValue, dbusName, newValue] = vectorItem; ++ updateProperty(sensorInterface, oldValue, newValue, dbusName); + } + } + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0002-Fix-PECI-client-creation-flow.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0002-Fix-PECI-client-creation-flow.patch new file mode 100644 index 000000000..cfdc99d66 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0002-Fix-PECI-client-creation-flow.patch @@ -0,0 +1,159 @@ +From 0a1b2a13f6dbc64b5851ac2b1ca99d57afa78d60 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 27 Jan 2021 15:52:16 -0800 +Subject: [PATCH] Fix PECI client creation flow + +This commit fixes the PECI client creation flow to make it retry +the creation when the client is not exposed correctly. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + src/CPUSensorMain.cpp | 66 +++++++++++++++++++++++++++++++++++-------- + 1 file changed, 54 insertions(+), 12 deletions(-) + +diff --git a/src/CPUSensorMain.cpp b/src/CPUSensorMain.cpp +index f304e3f..92c1716 100644 +--- a/src/CPUSensorMain.cpp ++++ b/src/CPUSensorMain.cpp +@@ -82,6 +82,7 @@ struct CPUConfig + }; + + static constexpr const char* peciDev = "/dev/peci-"; ++static constexpr const char* peciDevPath = "/sys/bus/peci/devices/"; + static constexpr const unsigned int rankNumMax = 8; + + namespace fs = std::filesystem; +@@ -167,7 +168,7 @@ bool createSensors(boost::asio::io_service& io, + } + + std::vector<fs::path> hwmonNamePaths; +- if (!findFiles(fs::path(R"(/sys/bus/peci/devices)"), ++ if (!findFiles(fs::path(peciDevPath), + R"(peci-\d+/\d+-.+/peci-.+/hwmon/hwmon\d+/name$)", + hwmonNamePaths, 6)) + { +@@ -403,7 +404,7 @@ bool createSensors(boost::asio::io_service& io, + return true; + } + +-void exportDevice(const CPUConfig& config) ++int exportDevice(const CPUConfig& config) + { + std::ostringstream hex; + hex << std::hex << config.addr; +@@ -411,9 +412,12 @@ void exportDevice(const CPUConfig& config) + std::string busStr = std::to_string(config.bus); + + std::string parameters = "peci-client 0x" + addrHexStr; +- std::string device = "/sys/bus/peci/devices/peci-" + busStr + "/new_device"; ++ std::string devPath = peciDevPath; ++ std::string delDevice = devPath + "peci-" + busStr + "/delete_device"; ++ std::string newDevice = devPath + "peci-" + busStr + "/new_device"; ++ std::string newClient = devPath + busStr + "-" + addrHexStr + "/driver"; + +- std::filesystem::path devicePath(device); ++ std::filesystem::path devicePath(newDevice); + const std::string& dir = devicePath.parent_path().string(); + for (const auto& path : std::filesystem::directory_iterator(dir)) + { +@@ -431,20 +435,38 @@ void exportDevice(const CPUConfig& config) + std::cout << parameters << " on bus " << busStr + << " is already exported\n"; + } +- return; ++ ++ std::ofstream delDeviceFile(delDevice); ++ if (!delDeviceFile.good()) ++ { ++ std::cerr << "Error opening " << delDevice << "\n"; ++ return -1; ++ } ++ delDeviceFile << parameters; ++ delDeviceFile.close(); ++ ++ break; + } + } + +- std::ofstream deviceFile(device); ++ std::ofstream deviceFile(newDevice); + if (!deviceFile.good()) + { +- std::cerr << "Error writing " << device << "\n"; +- return; ++ std::cerr << "Error opening " << newDevice << "\n"; ++ return -1; + } + deviceFile << parameters; + deviceFile.close(); + ++ if (!std::filesystem::exists(newClient)) ++ { ++ std::cerr << "Error creating " << newClient << "\n"; ++ return -1; ++ } ++ + std::cout << parameters << " on bus " << busStr << " is exported\n"; ++ ++ return 0; + } + + void detectCpu(boost::asio::deadline_timer& pingTimer, +@@ -460,6 +482,11 @@ void detectCpu(boost::asio::deadline_timer& pingTimer, + + for (CPUConfig& config : cpuConfigs) + { ++ if (config.state == State::READY) ++ { ++ continue; ++ } ++ + std::string peciDevPath = peciDev + std::to_string(config.bus); + auto file = open(peciDevPath.c_str(), O_RDWR | O_CLOEXEC); + if (file < 0) +@@ -510,16 +537,29 @@ void detectCpu(boost::asio::deadline_timer& pingTimer, + newState = State::OFF; + } + +- close(file); +- + if (config.state != newState) + { + if (newState != State::OFF) + { + if (config.state == State::OFF) + { +- std::cout << config.name << " is detected\n"; +- exportDevice(config); ++ struct peci_rd_pkg_cfg_msg msg; ++ msg.addr = config.addr; ++ msg.index = PECI_MBX_INDEX_CPU_ID; ++ msg.param = 0; ++ msg.rx_len = 4; ++ if (!ioctl(file, PECI_IOC_RD_PKG_CFG, &msg)) ++ { ++ std::cout << config.name << " is detected\n"; ++ if (exportDevice(config)) ++ { ++ newState = State::OFF; ++ } ++ } ++ else ++ { ++ newState = State::OFF; ++ } + } + + if (newState == State::ON) +@@ -542,6 +582,8 @@ void detectCpu(boost::asio::deadline_timer& pingTimer, + keepPinging = true; + } + ++ close(file); ++ + if (debug) + { + std::cout << config.name << ", state: " << config.state << "\n"; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0003-Fix-missing-threshold-de-assert-event-when-threshold.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0003-Fix-missing-threshold-de-assert-event-when-threshold.patch new file mode 100644 index 000000000..1cba1095d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0003-Fix-missing-threshold-de-assert-event-when-threshold.patch @@ -0,0 +1,143 @@ +From db4353de222b51726c8e3c765cc8f1df4ad67687 Mon Sep 17 00:00:00 2001 +From: Zhikui Ren <zhikui.ren@intel.com> +Date: Tue, 22 Jun 2021 11:35:12 -0700 +Subject: [PATCH] Fix missing de-assert event when threshold changes + +Issue: +Sensor can be re-constructed when sensor configuration changes +like a new threshold value. Threshold deassert can be missed +if the new threshold value fixes the alarm because the +default state for new threshold interface is de-asserted. + +Resolution: +Add a member variable hadValidSensor that is initialized to false +for new sensor. When hadValidSensor is false, threshold property changed +message will be emitted even if threshold property did not change, +If the previous sensor instance had the threshold raised, +Phosphor-sel-logger would notice the change and log a de-assert event. +If the previous sensor instance did not have the threshold raised, +Phosphor-sel-logger would notice this is not a change and not create +new SEL log. +Set hadValidSensor to true when sensor value is updated with a value +that is not NaN. This is done after threshold property changed message +is emitted. + +Tested: +1. Change threshold value for a voltage sensor to force a SEL. + ipmitool raw 0x04 0x26 0x60 0x1b 0x95 0x6b 0x00 0x99 0xa6 0x00 + +2. Verify SEL logged threshold assert event as expected + ipmitool sel list + 1 | Pre-Init |0000007277| Voltage #0x60 | Upper Non-critical going high | Asserted + +3. Use ipmitool to change threshold value back to normal + ipmitool raw 0x04 0x26 0x60 0x1b 0x95 0x6b 0x00 0xa4 0xa6 0x00 + +4. Verify SEL logged threshold de-assert event as expected + ipmitool sel list + 1 | Pre-Init |0000007277| Voltage #0x60 | Upper Non-critical going high | Asserted + 2 | Pre-Init |0000007304| Voltage #0x60 | Upper Non-critical going high | Deasserted + +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +--- + include/Thresholds.hpp | 2 +- + include/sensor.hpp | 2 ++ + src/Thresholds.cpp | 20 ++++++++++++++++---- + 3 files changed, 19 insertions(+), 5 deletions(-) + +diff --git a/include/Thresholds.hpp b/include/Thresholds.hpp +index af63f72..fd507d0 100644 +--- a/include/Thresholds.hpp ++++ b/include/Thresholds.hpp +@@ -44,7 +44,7 @@ struct Threshold + + void assertThresholds(Sensor* sensor, double assertValue, + thresholds::Level level, thresholds::Direction direction, +- bool assert); ++ bool assert, bool force = false); + + struct TimerUsed + { +diff --git a/include/sensor.hpp b/include/sensor.hpp +index b98241b..6235674 100644 +--- a/include/sensor.hpp ++++ b/include/sensor.hpp +@@ -71,6 +71,7 @@ struct Sensor + std::shared_ptr<sdbusplus::asio::dbus_interface> operationalInterface; + double value = std::numeric_limits<double>::quiet_NaN(); + double rawValue = std::numeric_limits<double>::quiet_NaN(); ++ bool hadValidValue = false; + bool overriddenState = false; + bool internalSet = false; + double hysteresisTrigger; +@@ -432,6 +433,7 @@ struct Sensor + { + markFunctional(true); + markAvailable(true); ++ hadValidValue = true; + } + } + +diff --git a/src/Thresholds.cpp b/src/Thresholds.cpp +index 821083a..da0d650 100644 +--- a/src/Thresholds.cpp ++++ b/src/Thresholds.cpp +@@ -418,10 +418,19 @@ bool checkThresholds(Sensor* sensor) + { + bool status = true; + std::vector<ChangeParam> changes = checkThresholds(sensor, sensor->value); ++ ++ // Sensor can be reconstructed when sensor configuration changes ++ // like a new threshold value. Threshold deassert can be missed ++ // if the new threshold value fixes the alarm because ++ // default state for new threshold interface is de-asserted. ++ // force sending assert/de-assert message when a not NaN value is updated ++ // for the first time even when threshold property did not change. ++ bool forceAssert = !sensor->hadValidValue; + for (const auto& change : changes) + { + assertThresholds(sensor, change.assertValue, change.threshold.level, +- change.threshold.direction, change.asserted); ++ change.threshold.direction, change.asserted, ++ forceAssert); + if (change.threshold.level == thresholds::Level::CRITICAL && + change.asserted) + { +@@ -443,6 +452,7 @@ void checkThresholdsPowerDelay(const std::weak_ptr<Sensor>& weakSensor, + + Sensor* sensor = sensorPtr.get(); + std::vector<ChangeParam> changes = checkThresholds(sensor, sensor->value); ++ bool forceAssert = !sensor->hadValidValue; + for (const auto& change : changes) + { + // When CPU is powered off, some volatges are expected to +@@ -467,13 +477,13 @@ void checkThresholdsPowerDelay(const std::weak_ptr<Sensor>& weakSensor, + } + } + assertThresholds(sensor, change.assertValue, change.threshold.level, +- change.threshold.direction, change.asserted); ++ change.threshold.direction, change.asserted, forceAssert); + } + } + + void assertThresholds(Sensor* sensor, double assertValue, + thresholds::Level level, thresholds::Direction direction, +- bool assert) ++ bool assert, bool force) + { + std::string property; + std::shared_ptr<sdbusplus::asio::dbus_interface> interface; +@@ -513,7 +523,9 @@ void assertThresholds(Sensor* sensor, double assertValue, + return; + } + +- if (interface->set_property<bool, true>(property, assert)) ++ bool propertyChanged = ++ interface->set_property<bool, true>(property, assert); ++ if (force || propertyChanged) + { + try + { +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0004-Fan-Tach-Sensor-Threshold-Ignore-Zero.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0004-Fan-Tach-Sensor-Threshold-Ignore-Zero.patch new file mode 100644 index 000000000..214fbe888 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0004-Fan-Tach-Sensor-Threshold-Ignore-Zero.patch @@ -0,0 +1,54 @@ +From 221aaf1431a01fefb5e7df2461a1c691738a50a7 Mon Sep 17 00:00:00 2001 +From: Zhikui Ren <zhikui.ren@intel.com> +Date: Fri, 19 Feb 2021 12:14:05 -0800 +Subject: [PATCH] Fan Tach Sensor Threshold Ignore Zero + +Currently there are systems that have system fans plugged +into different fan connectors. Fan present detection is +not supported in most of these systems. Critical low +threshold is asserted for the non-utilized fans +resulting in FSC boost all fans. + +Skip threshold checking for fan tach reading less or equal +to zero. This is a temporary WA until a more robust solution +is available. + +Note: with this workaround a completely non-working fan +will not be detected. FSC will still boost fans due to other +constraints if the system can't be cooled with the working fans. + +Tested: +No cr event for the missing fans. + +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +--- + src/TachSensor.cpp | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/src/TachSensor.cpp b/src/TachSensor.cpp +index c375dbf..e85a2a2 100644 +--- a/src/TachSensor.cpp ++++ b/src/TachSensor.cpp +@@ -186,10 +186,16 @@ void TachSensor::checkThresholds(void) + // WA - treat value <= 0 as not present + bool status = false; + +- if (redundancy && *redundancy) ++ if (value > 0) + { +- (*redundancy) +- ->update("/xyz/openbmc_project/sensors/fan_tach/" + name, !status); ++ status = thresholds::checkThresholds(this); ++ ++ if (redundancy && *redundancy) ++ { ++ (*redundancy) ++ ->update("/xyz/openbmc_project/sensors/fan_tach/" + name, ++ !status); ++ } + } + + bool curLed = !status; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0005-Fix-PECI-ioctl-number.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0005-Fix-PECI-ioctl-number.patch new file mode 100644 index 000000000..8119f7542 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0005-Fix-PECI-ioctl-number.patch @@ -0,0 +1,29 @@ +From f85dd776301371892ff5197c1995bf2224dd87ab Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Mon, 22 Feb 2021 15:57:20 -0800 +Subject: [PATCH] Fix PECI ioctl number + +This commit fixes PECI ioctl number to 0xb8 to avoid conflicts in +kernel v5.10. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + include/linux/peci-ioctl.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/linux/peci-ioctl.h b/include/linux/peci-ioctl.h +index e5b4b8bd3275..1f44edf4fc04 100644 +--- a/include/linux/peci-ioctl.h ++++ b/include/linux/peci-ioctl.h +@@ -601,7 +601,7 @@ struct peci_crashdump_get_frame_msg { + __u8 data[16]; + } __attribute__((__packed__)); + +-#define PECI_IOC_BASE 0xb7 ++#define PECI_IOC_BASE 0xb8 + + #define PECI_IOC_XFER \ + _IOWR(PECI_IOC_BASE, PECI_CMD_XFER, struct peci_xfer_msg) +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0006-CPUSensor-create-RequirediTempSensor-if-defined.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0006-CPUSensor-create-RequirediTempSensor-if-defined.patch new file mode 100644 index 000000000..2083adfef --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0006-CPUSensor-create-RequirediTempSensor-if-defined.patch @@ -0,0 +1,233 @@ +From 6ace96be5a7b6763545c1dfc572f8e2790d99d4b Mon Sep 17 00:00:00 2001 +From: Zhikui Ren <zhikui.ren@intel.com> +Date: Tue, 11 May 2021 11:14:55 -0700 +Subject: [PATCH] CPUSensor: create RequiredTempSensor if defined + +When BMC fails to talk to CPU through PECI, no +CPU sensors are created. Fan speed control only boost +fan when input sensor is not available. It does not +have the knowledge of which sensors are expected to +be present and can't treat "missing" sensor as failed one. +The failure on PECI goes unnoticed and we can have a thermal +run away. + +Query sensor config for RequiredTempSensor for any present +CPUs. Create a CPU sensor that is not available. +This sensor will be replaced by a normal CPU sensor when +peci detect the CPU and associated hwmon file is discovered. + +This is an initial patch that target to address the particular +failure case. More work will follow to support a more generic +"Required" sensor config. + +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +--- + include/CPUSensor.hpp | 9 ++++++ + src/CPUSensor.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++ + src/CPUSensorMain.cpp | 54 +++++++++++++++++++++++++++++----- + 3 files changed, 123 insertions(+), 8 deletions(-) + +diff --git a/include/CPUSensor.hpp b/include/CPUSensor.hpp +index 8b51b76..93b7fcc 100644 +--- a/include/CPUSensor.hpp ++++ b/include/CPUSensor.hpp +@@ -26,6 +26,15 @@ class CPUSensor : public Sensor + std::vector<thresholds::Threshold>&& thresholds, + const std::string& configuration, int cpuId, bool show, + double dtsOffset); ++ ++ // Create a CPUSensor without a path to sensor value ++ CPUSensor(const std::string& objectType, ++ sdbusplus::asio::object_server& objectServer, ++ std::shared_ptr<sdbusplus::asio::connection>& conn, ++ boost::asio::io_service& io, const std::string& sensorName, ++ std::vector<thresholds::Threshold>&& thresholdsIn, ++ const std::string& sensorConfiguration); ++ + ~CPUSensor() override; + static constexpr unsigned int sensorScaleFactor = 1000; + static constexpr unsigned int sensorPollMs = 1000; +diff --git a/src/CPUSensor.cpp b/src/CPUSensor.cpp +index c330088..3861ade 100644 +--- a/src/CPUSensor.cpp ++++ b/src/CPUSensor.cpp +@@ -98,6 +98,74 @@ CPUSensor::CPUSensor(const std::string& path, const std::string& objectType, + setupRead(); + } + ++// Create a dummy "not available" CPUSensor ++// This is used to indicate a missing required sensor for ++// other services like fan control ++CPUSensor::CPUSensor(const std::string& objectType, ++ sdbusplus::asio::object_server& objectServer, ++ std::shared_ptr<sdbusplus::asio::connection>& conn, ++ boost::asio::io_service& io, const std::string& sensorName, ++ std::vector<thresholds::Threshold>&& thresholdsIn, ++ const std::string& sensorConfiguration) : ++ Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration, ++ objectType, false, false, 0, 0, conn, PowerState::on), ++ objServer(objectServer), inputDev(io), waitTimer(io), ++ privTcontrol(std::numeric_limits<double>::quiet_NaN()), dtsOffset(0), ++ show(true), pollTime(CPUSensor::sensorPollMs), minMaxReadCounter(0) ++{ ++ // assume it is a temperature sensor for now ++ // support for other type can be added later ++ std::string interfacePath = ++ "/xyz/openbmc_project/sensors/temperature/" + name; ++ const char* units = sensor_paths::unitDegreesC; ++ minValue = -128; ++ maxValue = 127; ++ ++ sensorInterface = objectServer.add_interface( ++ interfacePath, "xyz.openbmc_project.Sensor.Value"); ++ ++ sensorInterface->register_property("Unit", units); ++ sensorInterface->register_property("MaxValue", maxValue); ++ sensorInterface->register_property("MinValue", minValue); ++ sensorInterface->register_property( ++ "Value", value, [&](const double& newValue, double& oldValue) { ++ return setSensorValue(newValue, oldValue); ++ }); ++ if (!sensorInterface->initialize()) ++ { ++ std::cerr << "error initializing value interface\n"; ++ } ++ ++ if (!availableInterface) ++ { ++ availableInterface = std::make_shared<sdbusplus::asio::dbus_interface>( ++ conn, sensorInterface->get_object_path(), availableInterfaceName); ++ availableInterface->register_property( ++ "Available", false, [this](const bool propIn, bool& old) { ++ if (propIn == old) ++ { ++ return 1; ++ } ++ old = propIn; ++ if (!propIn) ++ { ++ updateValue(std::numeric_limits<double>::quiet_NaN()); ++ } ++ return 1; ++ }); ++ availableInterface->initialize(); ++ } ++ if (!operationalInterface) ++ { ++ operationalInterface = ++ std::make_shared<sdbusplus::asio::dbus_interface>( ++ conn, sensorInterface->get_object_path(), ++ operationalInterfaceName); ++ operationalInterface->register_property("Functional", true); ++ operationalInterface->initialize(); ++ } ++} ++ + CPUSensor::~CPUSensor() + { + // close the input dev to cancel async operations +diff --git a/src/CPUSensorMain.cpp b/src/CPUSensorMain.cpp +index 0d94e4b..1d12fa6 100644 +--- a/src/CPUSensorMain.cpp ++++ b/src/CPUSensorMain.cpp +@@ -332,10 +332,9 @@ bool createSensors(boost::asio::io_service& io, + { + if (debug) + { +- std::cout << "Skipped: " << inputPath << ": " << sensorName +- << " is already created\n"; ++ std::cout << "Will be replaced: " << inputPath << ": " ++ << sensorName << " is already created\n"; + } +- continue; + } + + // check hidden properties +@@ -636,9 +635,9 @@ void detectCpuAsync( + }); + } + +-bool getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection>& systemBus, ++bool getCpuConfig(std::shared_ptr<sdbusplus::asio::connection>& systemBus, + boost::container::flat_set<CPUConfig>& cpuConfigs, +- ManagedObjectType& sensorConfigs, ++ ManagedObjectType& sensorConfigs, boost::asio::io_service& io, + sdbusplus::asio::object_server& objectServer) + { + bool useCache = false; +@@ -700,6 +699,45 @@ bool getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection>& systemBus, + iface->register_property("Present", *present); + iface->initialize(); + inventoryIfaces[name] = std::move(iface); ++ if (*present) ++ { ++ // create required CPU sensors here in unavailable state ++ auto findRequiredTempSensor = ++ config.second.find("RequiredTempSensor"); ++ auto findCpuId = config.second.find("CpuID"); ++ if (findRequiredTempSensor != config.second.end() && ++ findCpuId != config.second.end()) ++ { ++ std::string label = ++ std::visit(VariantToStringVisitor(), ++ findRequiredTempSensor->second); ++ // for temp sensor hwmon sysfs use input ++ std::string item{"input"}; ++ int cpuId = ++ std::visit(VariantToUnsignedIntVisitor(), ++ findCpuId->second); ++ std::string requiredSensorName = ++ createSensorName(label, item, cpuId); ++ ++ auto& sensorPtr = gCpuSensors[requiredSensorName]; ++ if (sensorPtr == nullptr) ++ { ++ // created a dummy sensor for required sensor, ++ // will be replaced with a real one if it is ++ // detected ++ std::string objectType{}; ++ std::vector<thresholds::Threshold> ++ emptyThreshold{}; ++ std::string emptyConfig{}; ++ sensorPtr = std::make_unique<CPUSensor>( ++ objectType, objectServer, systemBus, io, ++ requiredSensorName, ++ std::move(emptyThreshold), emptyConfig); ++ std::cout << "created required CPU sensor " ++ << requiredSensorName << "\n"; ++ } ++ } ++ } + } + + auto findBus = config.second.find("Bus"); +@@ -728,7 +766,6 @@ bool getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection>& systemBus, + std::cout << "name: " << name << "\n"; + std::cout << "type: " << type << "\n"; + } +- + cpuConfigs.emplace(bus, addr, name, State::OFF); + } + } +@@ -764,7 +801,8 @@ int main() + return; // we're being canceled + } + +- if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, objectServer)) ++ if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, io, ++ objectServer)) + { + detectCpuAsync(pingTimer, creationTimer, io, objectServer, + systemBus, cpuConfigs, sensorConfigs); +@@ -792,7 +830,7 @@ int main() + return; // we're being canceled + } + +- if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, ++ if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, io, + objectServer)) + { + detectCpuAsync(pingTimer, creationTimer, io, objectServer, +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0007-Add-support-for-the-energy-hwmon-type.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0007-Add-support-for-the-energy-hwmon-type.patch new file mode 100644 index 000000000..daaca7fae --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0007-Add-support-for-the-energy-hwmon-type.patch @@ -0,0 +1,266 @@ +From 9f5ef2e8d9c34d9d9ddce34d450aaedd5c122b22 Mon Sep 17 00:00:00 2001 +From: Szymon Dompke <szymon.dompke@intel.com> +Date: Tue, 18 May 2021 05:22:33 +0200 +Subject: [PATCH] Add support for the energy hwmon type +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this commit CPUSensors should be able detect hwmon files of type +‘energy’ described here: + + https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface + +These files hold a cumulative energy [micro Joule]. +Values read from these type of files will be exposed on dbus as a new +sensor. An example: + +└─/xyz + └─/xyz/openbmc_project + └─/xyz/openbmc_project/sensors + ├─/xyz/openbmc_project/sensors/energy + │ └─/xyz/openbmc_project/sensors/energy/Cumulative_Energy_CPU1 + +The energy counter will have different scale factor and different +default min/max values than other types of CPU sensors (power/temp). + +Tested: + Tested on physical machine where the `energy_input` files were present, + works as desired no regression detected. + +Authored-by: Zbigniew Kurzynski <zbigniew.kurzynski@intel.com> +Signed-off-by: Szymon Dompke <szymon.dompke@intel.com> +--- + include/CPUSensor.hpp | 13 +++++++-- + src/CPUSensor.cpp | 68 +++++++++++++++---------------------------- + src/CPUSensorMain.cpp | 30 ++++++++++++++++--- + 3 files changed, 60 insertions(+), 51 deletions(-) + +diff --git a/include/CPUSensor.hpp b/include/CPUSensor.hpp +index 93b7fcc..76e43dc 100644 +--- a/include/CPUSensor.hpp ++++ b/include/CPUSensor.hpp +@@ -16,6 +16,15 @@ + #include <variant> + #include <vector> + ++struct SensorProperties ++{ ++ std::string path; ++ std::string units; ++ double max; ++ double min; ++ unsigned int scaleFactor; ++}; ++ + class CPUSensor : public Sensor + { + public: +@@ -25,7 +34,7 @@ class CPUSensor : public Sensor + boost::asio::io_service& io, const std::string& sensorName, + std::vector<thresholds::Threshold>&& thresholds, + const std::string& configuration, int cpuId, bool show, +- double dtsOffset); ++ double dtsOffset, const SensorProperties& sensorProperties); + + // Create a CPUSensor without a path to sensor value + CPUSensor(const std::string& objectType, +@@ -36,7 +45,6 @@ class CPUSensor : public Sensor + const std::string& sensorConfiguration); + + ~CPUSensor() override; +- static constexpr unsigned int sensorScaleFactor = 1000; + static constexpr unsigned int sensorPollMs = 1000; + static constexpr size_t warnAfterErrorCount = 10; + static constexpr const char* labelTcontrol = "Tcontrol"; +@@ -54,6 +62,7 @@ class CPUSensor : public Sensor + size_t pollTime; + bool loggedInterfaceDown = false; + uint8_t minMaxReadCounter; ++ unsigned int scaleFactor; + void setupRead(void); + void handleResponse(const boost::system::error_code& err); + void checkThresholds(void) override; +diff --git a/src/CPUSensor.cpp b/src/CPUSensor.cpp +index 3861ade..6737151 100644 +--- a/src/CPUSensor.cpp ++++ b/src/CPUSensor.cpp +@@ -39,58 +39,37 @@ CPUSensor::CPUSensor(const std::string& path, const std::string& objectType, + boost::asio::io_service& io, const std::string& sensorName, + std::vector<thresholds::Threshold>&& thresholdsIn, + const std::string& sensorConfiguration, int cpuId, +- bool show, double dtsOffset) : ++ bool show, double dtsOffset, ++ const SensorProperties& sensorProperties) : + Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration, +- objectType, false, false, 0, 0, conn, PowerState::on), ++ objectType, false, false, sensorProperties.max, sensorProperties.min, ++ conn, PowerState::on), + objServer(objectServer), inputDev(io), waitTimer(io), path(path), + privTcontrol(std::numeric_limits<double>::quiet_NaN()), + dtsOffset(dtsOffset), show(show), pollTime(CPUSensor::sensorPollMs), +- minMaxReadCounter(0) ++ minMaxReadCounter(0), scaleFactor(sensorProperties.scaleFactor) + { + nameTcontrol = labelTcontrol; + nameTcontrol += " CPU" + std::to_string(cpuId); + if (show) + { +- if (auto fileParts = splitFileName(path)) ++ std::string interfacePath = sensorProperties.path + name; ++ sensorInterface = objectServer.add_interface( ++ interfacePath, "xyz.openbmc_project.Sensor.Value"); ++ if (thresholds::hasWarningInterface(thresholds)) + { +- auto& [type, nr, item] = *fileParts; +- std::string interfacePath; +- const char* units; +- if (type.compare("power") == 0) +- { +- interfacePath = "/xyz/openbmc_project/sensors/power/" + name; +- units = sensor_paths::unitWatts; +- minValue = 0; +- maxValue = 511; +- } +- else +- { +- interfacePath = +- "/xyz/openbmc_project/sensors/temperature/" + name; +- units = sensor_paths::unitDegreesC; +- minValue = -128; +- maxValue = 127; +- } +- +- sensorInterface = objectServer.add_interface( +- interfacePath, "xyz.openbmc_project.Sensor.Value"); +- if (thresholds::hasWarningInterface(thresholds)) +- { +- thresholdInterfaceWarning = objectServer.add_interface( +- interfacePath, +- "xyz.openbmc_project.Sensor.Threshold.Warning"); +- } +- if (thresholds::hasCriticalInterface(thresholds)) +- { +- thresholdInterfaceCritical = objectServer.add_interface( +- interfacePath, +- "xyz.openbmc_project.Sensor.Threshold.Critical"); +- } +- association = objectServer.add_interface(interfacePath, +- association::interface); +- +- setInitialProperties(conn, units); ++ thresholdInterfaceWarning = objectServer.add_interface( ++ interfacePath, "xyz.openbmc_project.Sensor.Threshold.Warning"); + } ++ if (thresholds::hasCriticalInterface(thresholds)) ++ { ++ thresholdInterfaceCritical = objectServer.add_interface( ++ interfacePath, "xyz.openbmc_project.Sensor.Threshold.Critical"); ++ } ++ association = ++ objectServer.add_interface(interfacePath, association::interface); ++ ++ setInitialProperties(conn, sensorProperties.units); + } + + // call setup always as not all sensors call setInitialProperties +@@ -248,7 +227,7 @@ void CPUSensor::updateMinMaxValues(void) + auto& [suffix, oldValue, dbusName, newValue] = vectorItem; + auto attrPath = boost::replace_all_copy(path, fileItem, suffix); + +- if(auto tmp = readFile(attrPath, CPUSensor::sensorScaleFactor)) ++ if (auto tmp = readFile(attrPath, scaleFactor)) + { + newValue.get() = *tmp; + } +@@ -302,7 +281,7 @@ void CPUSensor::handleResponse(const boost::system::error_code& err) + std::getline(responseStream, response); + rawValue = std::stod(response); + responseStream.clear(); +- double nvalue = rawValue / CPUSensor::sensorScaleFactor; ++ double nvalue = rawValue / scaleFactor; + + if (show) + { +@@ -328,8 +307,7 @@ void CPUSensor::handleResponse(const boost::system::error_code& err) + { + std::vector<thresholds::Threshold> newThresholds; + if (parseThresholdsFromAttr(newThresholds, path, +- CPUSensor::sensorScaleFactor, +- dtsOffset)) ++ scaleFactor, dtsOffset)) + { + if (!std::equal(thresholds.begin(), thresholds.end(), + newThresholds.begin(), +diff --git a/src/CPUSensorMain.cpp b/src/CPUSensorMain.cpp +index 1d12fa6..e348aa7 100644 +--- a/src/CPUSensorMain.cpp ++++ b/src/CPUSensorMain.cpp +@@ -94,6 +94,18 @@ static constexpr auto sensorTypes{std::to_array<const char*>({"XeonCPU"})}; + static constexpr auto hiddenProps{std::to_array<const char*>( + {CPUSensor::labelTcontrol, "Tthrottle", "Tjmax"})}; + ++static const boost::container::flat_map<std::string, SensorProperties> ++ sensorPropertiesMap = { ++ {"power", ++ {"/xyz/openbmc_project/sensors/power/", sensor_paths::unitWatts, 511, ++ 0, 1000}}, ++ {"energy", ++ {"/xyz/openbmc_project/sensors/energy/", sensor_paths::unitJoules, ++ std::numeric_limits<uint32_t>::max() / 1000000, 0.0, 1000000}}, ++ {"temp", ++ {"/xyz/openbmc_project/sensors/temperature/", ++ sensor_paths::unitDegreesC, 127.0, -128.0, 1000}}}; ++ + void detectCpuAsync( + boost::asio::deadline_timer& pingTimer, + boost::asio::deadline_timer& creationTimer, boost::asio::io_service& io, +@@ -296,7 +308,8 @@ bool createSensors(boost::asio::io_service& io, + + auto directory = hwmonNamePath.parent_path(); + std::vector<fs::path> inputPaths; +- if (!findFiles(directory, R"((temp|power)\d+_(input|average|cap)$)", ++ if (!findFiles(directory, ++ R"((temp|power|energy)\d+_(input|average|cap)$)", + inputPaths, 0)) + { + std::cerr << "No temperature sensors in system\n"; +@@ -364,6 +377,16 @@ bool createSensors(boost::asio::io_service& io, + } + } + ++ const auto& it = sensorPropertiesMap.find(type); ++ if (it == sensorPropertiesMap.end()) ++ { ++ std::cerr ++ << "Failure getting sensor properties for sensor type: " ++ << type << "\n"; ++ continue; ++ } ++ const SensorProperties& prop = it->second; ++ + std::vector<thresholds::Threshold> sensorThresholds; + std::string labelHead = label.substr(0, label.find(' ')); + parseThresholdsFromConfig(*sensorData, sensorThresholds, +@@ -371,8 +394,7 @@ bool createSensors(boost::asio::io_service& io, + if (sensorThresholds.empty()) + { + if (!parseThresholdsFromAttr(sensorThresholds, inputPathStr, +- CPUSensor::sensorScaleFactor, +- dtsOffset)) ++ prop.scaleFactor, dtsOffset)) + { + std::cerr << "error populating thresholds for " + << sensorName << "\n"; +@@ -384,7 +406,7 @@ bool createSensors(boost::asio::io_service& io, + sensorPtr = std::make_unique<CPUSensor>( + inputPathStr, sensorType, objectServer, dbusConnection, io, + sensorName, std::move(sensorThresholds), *interfacePath, cpuId, +- show, dtsOffset); ++ show, dtsOffset, prop); + createdSensors.insert(sensorName); + if (debug) + { +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0008-CPUSensor-additional-debug-message.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0008-CPUSensor-additional-debug-message.patch new file mode 100644 index 000000000..40c8d46bd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0008-CPUSensor-additional-debug-message.patch @@ -0,0 +1,69 @@ +From c045d0ace218a8f0c9e9af0b04aed24ec733fc79 Mon Sep 17 00:00:00 2001 +From: Zhikui Ren <zhikui.ren@intel.com> +Date: Tue, 22 Jun 2021 14:49:44 -0700 +Subject: [PATCH] CPUSensor: additional debug message + +Add debug message to capture more information on threshold changes. + +Example output - DTS threshold changes when Tcontrol was first read + Jan 01 00:06:06 intel-obmc cpusensor[461]: Core_16_CPU1: Tcontrol changed from nan to 92 + Jan 01 00:06:06 intel-obmc cpusensor[461]: Core_22_CPU1: Tcontrol changed from nan to 92 + Jan 01 00:06:06 intel-obmc cpusensor[461]: Core_24_CPU1: Tcontrol changed from nan to 92 + Jan 01 00:06:06 intel-obmc cpusensor[461]: DTS_CPU1: Tcontrol changed from nan to 92 + Jan 01 00:06:06 intel-obmc cpusensor[461]: Threshold: /sys/bus/peci/devices/peci-0/0-30/peci-cputemp.0/hwmon/hwmon12/temp2_max: 92 + Jan 01 00:06:06 intel-obmc cpusensor[461]: Threshold: /sys/bus/peci/devices/peci-0/0-30/peci-cputemp.0/hwmon/hwmon12/temp2_crit: 100 + Jan 01 00:06:06 intel-obmc cpusensor[461]: DTS_CPU1: new threshold value 92 + Jan 01 00:06:06 intel-obmc cpusensor[461]: DTS_CPU1: new threshold value 100 + +The above message will be logged when BMC reset or host resets. + +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +--- + src/CPUSensor.cpp | 5 +++++ + src/Thresholds.cpp | 7 ++----- + 2 files changed, 7 insertions(+), 5 deletions(-) + +diff --git a/src/CPUSensor.cpp b/src/CPUSensor.cpp +index 0621e04..65acdac 100644 +--- a/src/CPUSensor.cpp ++++ b/src/CPUSensor.cpp +@@ -300,6 +300,7 @@ void CPUSensor::handleResponse(const boost::system::error_code& err) + : std::numeric_limits<double>::quiet_NaN(); + if (gTcontrol != privTcontrol) + { ++ std::cout << name << ": Tcontrol changed from " << privTcontrol << " to " << gTcontrol << "\n"; + privTcontrol = gTcontrol; + + if (!thresholds.empty()) +@@ -318,6 +319,10 @@ void CPUSensor::handleResponse(const boost::system::error_code& err) + thresholds::updateThresholds(this); + } + } ++ for (auto& threshold : thresholds) ++ { ++ std::cout << name << ": new threshold value " << threshold.value << "\n"; ++ } + } + else + { +diff --git a/src/Thresholds.cpp b/src/Thresholds.cpp +index 78ded55..283dacf 100644 +--- a/src/Thresholds.cpp ++++ b/src/Thresholds.cpp +@@ -583,11 +583,8 @@ bool parseThresholdsFromAttr( + if (auto val = readFile(attrPath, scaleFactor)) + { + *val += offset; +- if (debug) +- { +- std::cout << "Threshold: " << attrPath << ": " << *val +- << "\n"; +- } ++ std::cout << "Threshold: " << attrPath << ": " << *val ++ << "\n"; + thresholdVector.emplace_back(level, direction, *val); + } + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0009-CPUSensor-Create-CPUConfig-for-each-PECI-adapter.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0009-CPUSensor-Create-CPUConfig-for-each-PECI-adapter.patch new file mode 100644 index 000000000..737ba9128 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0009-CPUSensor-Create-CPUConfig-for-each-PECI-adapter.patch @@ -0,0 +1,147 @@ +From 262682632ee493d0b6593540cfc902d11286b7c3 Mon Sep 17 00:00:00 2001 +From: Iwona Winiarska <iwona.winiarska@intel.com> +Date: Tue, 13 Jul 2021 15:16:16 +0200 +Subject: [PATCH] CPUSensor: Create CPUConfig for each PECI adapter + +Currently CPUSensor is based on configuration that defines CPUs for the +specific PECI adapter (usually peci-wire). As a consequence, MFD devices +are created only for the defined adapter. + +Since duplicating static CPU records in configuration file for other +PECI adapters may be confusing to users, let's add CPUConfig for other +PECI adapters dynamically by detecting if there is more than one PECI +adapter driver bound in the system. + +Please note, that this change is limited to enabling HWMON driver, +sensors for all adapters will not be exposed on D-Bus. + +Tested: +No changes if only one PECI interface is available, HWMON drivers +creation will be based on provided configuration. +When both peci-aspeed (peci-wire) and peci-mctp are bound in the system, +HWMON drivers are created for each PECI interface. + +Signed-off-by: Iwona Winiarska <iwona.winiarska@intel.com> +--- + src/CPUSensorMain.cpp | 90 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 90 insertions(+) + +diff --git a/src/CPUSensorMain.cpp b/src/CPUSensorMain.cpp +index 744ca50..6850df7 100644 +--- a/src/CPUSensorMain.cpp ++++ b/src/CPUSensorMain.cpp +@@ -33,6 +33,7 @@ + #include <fstream> + #include <functional> + #include <memory> ++#include <optional> + #include <regex> + #include <sstream> + #include <stdexcept> +@@ -658,6 +659,91 @@ void detectCpuAsync( + }); + } + ++std::optional<uint64_t> getPeciDeviceNum(const fs::path& peciAdapterNamePath) ++{ ++ fs::path::iterator it = peciAdapterNamePath.begin(); ++ std::advance(it, 5); // /sys/bus/peci/devices/peci-xxxx ++ std::string peciDeviceName = *it; ++ auto pos = peciDeviceName.find('-'); ++ if (pos == std::string::npos) ++ { ++ std::cerr << "Incorrect PECI device name: " << peciDeviceName << "\n"; ++ return std::nullopt; ++ } ++ ++ try ++ { ++ return std::stoull(peciDeviceName.substr(pos + 1, 1)); ++ } ++ catch (std::logic_error&) ++ { ++ return std::nullopt; ++ } ++} ++ ++std::optional<std::string> ++ readPeciAdapterNameFromFile(const fs::path& peciAdapterNamePath) ++{ ++ std::ifstream nameFile(peciAdapterNamePath); ++ if (!nameFile.good()) ++ { ++ std::cerr << "Cannot read: " << peciAdapterNamePath << "\n"; ++ return std::nullopt; ++ } ++ ++ std::string peciAdapterName; ++ std::getline(nameFile, peciAdapterName); ++ nameFile.close(); ++ if (peciAdapterName.empty()) ++ { ++ return std::nullopt; ++ } ++ ++ auto pos = peciAdapterName.find('-'); ++ peciAdapterName = peciAdapterName.substr(pos + 1); ++ ++ return peciAdapterName; ++} ++ ++void addConfigsForOtherPeciAdapters( ++ boost::container::flat_set<CPUConfig>& cpuConfigs, uint64_t& bus, ++ uint64_t& addr, std::string& name, const State& state) ++{ ++ std::vector<fs::path> peciAdapterNamePaths; ++ if (!findFiles(fs::path(peciDevPath), R"(peci-\d+/name$)", ++ peciAdapterNamePaths, 1)) ++ { ++ std::cerr << "No PECI adapters in system\n"; ++ return; ++ } ++ ++ for (const fs::path& peciAdapterNamePath : peciAdapterNamePaths) ++ { ++ std::optional<uint64_t> peciDeviceNum = ++ getPeciDeviceNum(peciAdapterNamePath); ++ if (!peciDeviceNum || peciDeviceNum == bus) ++ { ++ continue; ++ } ++ ++ std::optional<std::string> peciAdapterName = ++ readPeciAdapterNameFromFile(peciAdapterNamePath); ++ if (!peciAdapterName) ++ { ++ continue; ++ } ++ ++ // Change result for peci-aspeed ++ if (peciAdapterName->compare("bus") == 0) ++ { ++ peciAdapterName = "wire"; ++ } ++ ++ cpuConfigs.emplace(*peciDeviceNum, addr, name + "_" + *peciAdapterName, ++ state); ++ } ++} ++ + bool getCpuConfig(std::shared_ptr<sdbusplus::asio::connection>& systemBus, + boost::container::flat_set<CPUConfig>& cpuConfigs, + ManagedObjectType& sensorConfigs, boost::asio::io_service& io, +@@ -789,7 +875,11 @@ bool getCpuConfig(std::shared_ptr<sdbusplus::asio::connection>& systemBus, + std::cout << "name: " << name << "\n"; + std::cout << "type: " << type << "\n"; + } ++ + cpuConfigs.emplace(bus, addr, name, State::OFF); ++ ++ addConfigsForOtherPeciAdapters(cpuConfigs, bus, addr, name, ++ State::OFF); + } + } + } +-- +2.31.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/intrusionsensor-depend-on-networkd.conf b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/intrusionsensor-depend-on-networkd.conf new file mode 100644 index 000000000..6f0fd3ffc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/intrusionsensor-depend-on-networkd.conf @@ -0,0 +1,3 @@ +[Unit] +After=systemd-networkd.service +Requires=systemd-networkd.service diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend new file mode 100644 index 000000000..765ad3739 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend @@ -0,0 +1,49 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" +PROJECT_SRC_DIR := "${THISDIR}/${PN}" + +SRCREV = "77518b28db824e01af18351094680a99b1ba3cae" +#SRC_URI = "git://github.com/openbmc/dbus-sensors.git" + +SRC_URI += "\ + file://intrusionsensor-depend-on-networkd.conf \ + file://0001-Add-check-for-min-max-received-from-hwmon-files.patch \ + file://0002-Fix-PECI-client-creation-flow.patch \ + file://0003-Fix-missing-threshold-de-assert-event-when-threshold.patch \ + file://0004-Fan-Tach-Sensor-Threshold-Ignore-Zero.patch \ + file://0005-Fix-PECI-ioctl-number.patch \ + file://0006-CPUSensor-create-RequirediTempSensor-if-defined.patch \ + file://0007-Add-support-for-the-energy-hwmon-type.patch \ + file://0008-CPUSensor-additional-debug-message.patch \ + file://0009-CPUSensor-Create-CPUConfig-for-each-PECI-adapter.patch \ + " + +DEPENDS:append = " libgpiod libmctp" + +PACKAGECONFIG += " \ + adcsensor \ + cpusensor \ + exitairtempsensor \ + fansensor \ + hwmontempsensor \ + intrusionsensor \ + ipmbsensor \ + mcutempsensor \ + psusensor \ +" + +PACKAGECONFIG[nvmesensor] = "-Dnvme=enabled, -Dnvme=disabled" + +# Enable Validation unsecure based on IMAGE_FEATURES +EXTRA_OEMESON += "${@bb.utils.contains('EXTRA_IMAGE_FEATURES', 'validation-unsecure', '-Dvalidate-unsecure-feature=enabled', '', d)}" + +SYSTEMD_SERVICE:${PN} += "${@bb.utils.contains('PACKAGECONFIG', 'nvmesensor', \ + 'xyz.openbmc_project.nvmesensor.service', \ + '', d)}" + +do_install:append() { + svc="xyz.openbmc_project.intrusionsensor.service" + srcf="${WORKDIR}/intrusionsensor-depend-on-networkd.conf" + dstf="${D}/etc/systemd/system/${svc}.d/10-depend-on-networkd.conf" + mkdir -p "${D}/etc/systemd/system/${svc}.d" + install "${srcf}" "${dstf}" +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/settings/settings_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/settings_git.bb new file mode 100644 index 000000000..703cbb803 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/settings_git.bb @@ -0,0 +1,20 @@ +SUMMARY = "Settings" + +SRC_URI = "git://github.com/Intel-BMC/settings.git;protocol=ssh" +SRCREV = "85a8be9a3fb8ef4726899b28f10fb9afa6fa9e89" +PV = "0.1+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SYSTEMD_SERVICE:${PN} = "xyz.openbmc_project.Settings.service" + +DEPENDS = "boost \ + nlohmann-json \ + sdbusplus" + +S = "${WORKDIR}/git" +inherit cmake systemd + +EXTRA_OECMAKE = "-DYOCTO=1" + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/special-mode-mgr/special-mode-mgr_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/special-mode-mgr/special-mode-mgr_git.bb new file mode 100644 index 000000000..d17aebf00 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/special-mode-mgr/special-mode-mgr_git.bb @@ -0,0 +1,31 @@ +SUMMARY = "Special mode manager daemon to handle manufacturing modes" +DESCRIPTION = "Daemon exposes the manufacturing mode property" + +PV = "1.0+git${SRCPV}" + +S = "${WORKDIR}/git" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SRC_URI = "git://github.com/Intel-BMC/special-mode-manager.git;protocol=ssh" +SRCREV = "42a0ba3c61ae38cc84b06705159065860492fc2e" + +EXTRA_OECMAKE += "${@bb.utils.contains('EXTRA_IMAGE_FEATURES', 'validation-unsecure', '-DBMC_VALIDATION_UNSECURE_FEATURE=ON', '', d)}" + +inherit cmake systemd +SYSTEMD_SERVICE:${PN} = "specialmodemgr.service" + +DEPENDS += " \ + systemd \ + sdbusplus \ + phosphor-logging \ + boost \ + libpam \ + libgpiod \ + " +RDEPENDS:${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-logging \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-post-code-manager_git.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-post-code-manager_git.bbappend new file mode 100644 index 000000000..a4e894cbc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-post-code-manager_git.bbappend @@ -0,0 +1,2 @@ +#SRC_URI = "git://github.com/openbmc/phosphor-post-code-manager.git" +SRCREV = "9ce5a645f50c0ab94e582abbf95474f636aba678" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend new file mode 100644 index 000000000..b7e8c0b22 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend @@ -0,0 +1,3 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +DEPENDS += "gtest" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/system/callback-manager.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/system/callback-manager.bb new file mode 100644 index 000000000..b123ddb35 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/system/callback-manager.bb @@ -0,0 +1,19 @@ +SUMMARY = "Callback Manager" +DESCRIPTION = "D-Bus daemon that registers matches that trigger method calls" + +SRC_URI = "git://github.com/openbmc/s2600wf-misc.git;protocol=ssh" + +inherit cmake systemd +DEPENDS = "boost sdbusplus" + +PV = "0.1+git${SRCPV}" +SRCREV = "0c5059f685f6df0704a4b773f2e617cf10d03210" + +S = "${WORKDIR}/git/callback-manager" + +SYSTEMD_SERVICE:${PN} += "callback-manager.service" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENCE;md5=7becf906c8f8d03c237bad13bc3dac53" + +EXTRA_OECMAKE = "-DYOCTO=1" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/telemetry/telemetry_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/telemetry/telemetry_%.bbappend new file mode 100644 index 000000000..ebd808168 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/telemetry/telemetry_%.bbappend @@ -0,0 +1,6 @@ +SRC_URI = "git://github.com/openbmc/telemetry.git" +SRCREV = "e28aa53dc1492f09a64dc9f1dbfd5b6dba06e31f" + +EXTRA_OEMESON += " -Dmax-reports=10" +EXTRA_OEMESON += " -Dmax-reading-parameters=200" +EXTRA_OEMESON += " -Dmin-interval=1000" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0005-Added-suport-for-multiple-user-manager-services.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0005-Added-suport-for-multiple-user-manager-services.patch new file mode 100644 index 000000000..a7f431049 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0005-Added-suport-for-multiple-user-manager-services.patch @@ -0,0 +1,1666 @@ +From 75971b8faf7ef7af7285ba7d5207be71c66e5d11 Mon Sep 17 00:00:00 2001 +From: Radivoje Jovanovic <radivoje.jovanovic@intel.com> +Date: Mon, 2 Jul 2018 19:23:25 -0700 +Subject: [PATCH] Added suport for multiple user manager services + +Support added for SSSD service implementation + +Signed-off-by: Alberto Salazar Perez <alberto.salazar.perez@intel.com> +Signed-off-by: Radivoje Jovanovic <radivoje.jovanovic@intel.com> +Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com> +--- + Makefile.am | 5 +- + mainapp.cpp | 90 +++++- + user_mgr.cpp | 297 ++---------------- + user_mgr.hpp | 9 +- + user_service.cpp | 789 +++++++++++++++++++++++++++++++++++++++++++++++ + user_service.hpp | 233 ++++++++++++++ + 6 files changed, 1149 insertions(+), 274 deletions(-) + create mode 100644 user_service.cpp + create mode 100644 user_service.hpp + +diff --git a/Makefile.am b/Makefile.am +index 1dbd594..fe47aaf 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,11 +1,12 @@ + bin_PROGRAMS = phosphor-user-manager + +-noinst_HEADERS = user_mgr.hpp users.hpp ++noinst_HEADERS = user_mgr.hpp users.hpp user_service.hpp + + phosphor_user_manager_SOURCES = \ + mainapp.cpp \ + user_mgr.cpp \ +- users.cpp ++ users.cpp \ ++ user_service.cpp + + phosphor_user_manager_LDFLAGS = $(SDBUSPLUS_LIBS) \ + $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ +diff --git a/mainapp.cpp b/mainapp.cpp +index e08da61..f4b7f8c 100644 +--- a/mainapp.cpp ++++ b/mainapp.cpp +@@ -16,18 +16,106 @@ + #include "config.h" + + #include "user_mgr.hpp" ++#include "user_service.hpp" + ++#include <getopt.h> ++ ++#include <iostream> + #include <string> + + // D-Bus root for user manager + constexpr auto USER_MANAGER_ROOT = "/xyz/openbmc_project/user"; + ++void printUsage() ++{ ++ std::string usage = ++ R"(Usage: ++ phosphor-user-manager [OPTIONS] ++ ++Backend DBUS service for OpenBMC User Management. ++If no OPTIONS are specified, shadow file will be used. ++ ++Options: ++ -s, --service={shadow|sssd} ++ Specify the authentication service to use: ++ 'shadow' will use the /etc/shadow file. ++ 'sssd' will use the sssd service domains. ++ -h, --help Displays this help message. ++)"; ++ std::cerr << usage; ++} ++ ++void parseArgs(int argc, char** argv, ++ phosphor::user::UserService::ServiceType& srvc) ++{ ++ const std::string shortOpts{"s:h"}; ++ const struct option longOpts[] = {{"service", 1, nullptr, 's'}, ++ {"help", 0, nullptr, 'h'}, ++ {nullptr, 0, nullptr, 0}}; ++ ++ while (true) ++ { ++ const auto opt = ++ getopt_long(argc, argv, shortOpts.c_str(), longOpts, nullptr); ++ ++ if (opt == -1) ++ { ++ if (srvc == phosphor::user::UserService::ServiceType::none) ++ { ++ srvc = phosphor::user::UserService::ServiceType::shadow; ++ } ++ break; ++ } ++ ++ switch (opt) ++ { ++ case 's': ++ { ++ std::string srvcStr{optarg}; ++ if (!srvcStr.compare("shadow")) ++ { ++ srvc = phosphor::user::UserService::ServiceType::shadow; ++ } ++ else if (!srvcStr.compare("sssd")) ++ { ++ srvc = phosphor::user::UserService::ServiceType::sssd; ++ } ++ else ++ { ++ std::cerr << "Error. '" << srvcStr << "' is not a valid" ++ << " authentication service." << std::endl; ++ printUsage(); ++ exit(1); ++ } ++ } ++ break; ++ ++ case 'h': ++ { ++ printUsage(); ++ exit(0); ++ } ++ ++ default: ++ { ++ printUsage(); ++ exit(1); ++ } ++ } ++ } ++} ++ + int main(int argc, char** argv) + { ++ // Check command line options. Exit if error. ++ phosphor::user::UserService::ServiceType srvc = ++ phosphor::user::UserService::ServiceType::none; ++ parseArgs(argc, argv, srvc); ++ + auto bus = sdbusplus::bus::new_default(); + sdbusplus::server::manager::manager objManager(bus, USER_MANAGER_ROOT); + +- phosphor::user::UserMgr userMgr(bus, USER_MANAGER_ROOT); ++ phosphor::user::UserMgr userMgr(bus, USER_MANAGER_ROOT, srvc); + + // Claim the bus now + bus.request_name(USER_MANAGER_BUSNAME); +diff --git a/user_mgr.cpp b/user_mgr.cpp +index 1b14e8e..f6f2fdb 100644 +--- a/user_mgr.cpp ++++ b/user_mgr.cpp +@@ -18,43 +18,34 @@ + + #include "user_mgr.hpp" + +-#include "file.hpp" + #include "shadowlock.hpp" + #include "users.hpp" + + #include <grp.h> + #include <pwd.h> +-#include <shadow.h> +-#include <sys/types.h> +-#include <sys/wait.h> + #include <time.h> +-#include <unistd.h> + + #include <boost/algorithm/string/split.hpp> +-#include <boost/process/child.hpp> +-#include <boost/process/io.hpp> + #include <phosphor-logging/elog-errors.hpp> + #include <phosphor-logging/elog.hpp> + #include <phosphor-logging/log.hpp> + #include <xyz/openbmc_project/Common/error.hpp> + #include <xyz/openbmc_project/User/Common/error.hpp> + +-#include <algorithm> ++#include <cstdio> + #include <fstream> +-#include <numeric> + #include <regex> ++#include <stdexcept> + + namespace phosphor + { + namespace user + { + +-static constexpr const char* passwdFileName = "/etc/passwd"; + static constexpr size_t ipmiMaxUsers = 15; + static constexpr size_t ipmiMaxUserNameLen = 16; + static constexpr size_t systemMaxUserNameLen = 30; + static constexpr size_t maxSystemUsers = 30; +-static constexpr const char* grpSsh = "ssh"; + static constexpr uint8_t minPasswdLength = 8; + static constexpr int success = 0; + static constexpr int failure = -1; +@@ -100,79 +91,6 @@ using NoResource = + + using Argument = xyz::openbmc_project::Common::InvalidArgument; + +-template <typename... ArgTypes> +-static std::vector<std::string> executeCmd(const char* path, +- ArgTypes&&... tArgs) +-{ +- std::vector<std::string> stdOutput; +- boost::process::ipstream stdOutStream; +- boost::process::child execProg(path, const_cast<char*>(tArgs)..., +- boost::process::std_out > stdOutStream); +- std::string stdOutLine; +- +- while (stdOutStream && std::getline(stdOutStream, stdOutLine) && +- !stdOutLine.empty()) +- { +- stdOutput.emplace_back(stdOutLine); +- } +- +- execProg.wait(); +- +- int retCode = execProg.exit_code(); +- if (retCode) +- { +- log<level::ERR>("Command execution failed", entry("PATH=%d", path), +- entry("RETURN_CODE:%d", retCode)); +- elog<InternalFailure>(); +- } +- +- return stdOutput; +-} +- +-static std::string getCSVFromVector(std::vector<std::string> vec) +-{ +- switch (vec.size()) +- { +- case 0: +- { +- return ""; +- } +- break; +- +- case 1: +- { +- return std::string{vec[0]}; +- } +- break; +- +- default: +- { +- return std::accumulate( +- std::next(vec.begin()), vec.end(), vec[0], +- [](std::string a, std::string b) { return a + ',' + b; }); +- } +- } +-} +- +-static bool removeStringFromCSV(std::string& csvStr, const std::string& delStr) +-{ +- std::string::size_type delStrPos = csvStr.find(delStr); +- if (delStrPos != std::string::npos) +- { +- // need to also delete the comma char +- if (delStrPos == 0) +- { +- csvStr.erase(delStrPos, delStr.size() + 1); +- } +- else +- { +- csvStr.erase(delStrPos - 1, delStr.size() + 1); +- } +- return true; +- } +- return false; +-} +- + bool UserMgr::isUserExist(const std::string& userName) + { + if (userName.empty()) +@@ -299,39 +217,14 @@ void UserMgr::createUser(std::string userName, + { + throwForInvalidPrivilege(priv); + throwForInvalidGroups(groupNames); +- // All user management lock has to be based on /etc/shadow +- // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; + throwForUserExists(userName); + throwForUserNameConstraints(userName, groupNames); + throwForMaxGrpUserCount(groupNames); + +- std::string groups = getCSVFromVector(groupNames); +- bool sshRequested = removeStringFromCSV(groups, grpSsh); +- +- // treat privilege as a group - This is to avoid using different file to +- // store the same. +- if (!priv.empty()) +- { +- if (groups.size() != 0) +- { +- groups += ","; +- } +- groups += priv; +- } +- try +- { +- executeCmd("/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(), +- "-m", "-N", "-s", +- (sshRequested ? "/bin/sh" : "/bin/nologin"), "-e", +- (enabled ? "" : "1970-01-02")); +- } +- catch (const InternalFailure& e) +- { +- log<level::ERR>("Unable to create new user"); +- elog<InternalFailure>(); +- } ++ // Tell the User Service to create a new user with the info provided. ++ userSrvc->createUser(userName, groupNames, priv, enabled); + +- // Add the users object before sending out the signal ++ // Add the users to the local list before sending out the signal + std::string userObj = std::string(usersObjPath) + "/" + userName; + std::sort(groupNames.begin(), groupNames.end()); + usersList.emplace( +@@ -345,19 +238,11 @@ void UserMgr::createUser(std::string userName, + + void UserMgr::deleteUser(std::string userName) + { +- // All user management lock has to be based on /etc/shadow +- // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; + throwForUserDoesNotExist(userName); +- try +- { +- executeCmd("/usr/sbin/userdel", userName.c_str(), "-r"); +- } +- catch (const InternalFailure& e) +- { +- log<level::ERR>("User delete failed", +- entry("USER_NAME=%s", userName.c_str())); +- elog<InternalFailure>(); +- } ++ ++ // Tell the User Service to delete user ++ userSrvc->deleteUser(userName); ++ // Then delete user from local list + + usersList.erase(userName); + +@@ -368,24 +253,13 @@ void UserMgr::deleteUser(std::string userName) + + void UserMgr::renameUser(std::string userName, std::string newUserName) + { +- // All user management lock has to be based on /etc/shadow +- // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; + throwForUserDoesNotExist(userName); + throwForUserExists(newUserName); + throwForUserNameConstraints(newUserName, + usersList[userName].get()->userGroups()); +- try +- { +- std::string newHomeDir = "/home/" + newUserName; +- executeCmd("/usr/sbin/usermod", "-l", newUserName.c_str(), +- userName.c_str(), "-d", newHomeDir.c_str(), "-m"); +- } +- catch (const InternalFailure& e) +- { +- log<level::ERR>("User rename failed", +- entry("USER_NAME=%s", userName.c_str())); +- elog<InternalFailure>(); +- } ++ // Call The User Service to rename user on the system ++ userSrvc->renameUser(userName, newUserName); ++ // Update local list to reflect the name change + const auto& user = usersList[userName]; + std::string priv = user.get()->userPrivilege(); + std::vector<std::string> groupNames = user.get()->userGroups(); +@@ -409,8 +283,6 @@ void UserMgr::updateGroupsAndPriv(const std::string& userName, + { + throwForInvalidPrivilege(priv); + throwForInvalidGroups(groupNames); +- // All user management lock has to be based on /etc/shadow +- // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; + throwForUserDoesNotExist(userName); + const std::vector<std::string>& oldGroupNames = + usersList[userName].get()->userGroups(); +@@ -426,29 +298,8 @@ void UserMgr::updateGroupsAndPriv(const std::string& userName, + throwForMaxGrpUserCount(groupNames); + } + +- std::string groups = getCSVFromVector(groupNames); +- bool sshRequested = removeStringFromCSV(groups, grpSsh); +- +- // treat privilege as a group - This is to avoid using different file to +- // store the same. +- if (!priv.empty()) +- { +- if (groups.size() != 0) +- { +- groups += ","; +- } +- groups += priv; +- } +- try +- { +- executeCmd("/usr/sbin/usermod", userName.c_str(), "-G", groups.c_str(), +- "-s", (sshRequested ? "/bin/sh" : "/bin/nologin")); +- } +- catch (const InternalFailure& e) +- { +- log<level::ERR>("Unable to modify user privilege / groups"); +- elog<InternalFailure>(); +- } ++ // Call The User Service to update user groups and priv on the system ++ userSrvc->updateGroupsAndPriv(userName, groupNames, priv); + + log<level::INFO>("User groups / privilege updated successfully", + entry("USER_NAME=%s", userName.c_str())); +@@ -644,19 +495,9 @@ int UserMgr::setPamModuleArgValue(const std::string& moduleName, + + void UserMgr::userEnable(const std::string& userName, bool enabled) + { +- // All user management lock has to be based on /etc/shadow +- // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; + throwForUserDoesNotExist(userName); +- try +- { +- executeCmd("/usr/sbin/usermod", userName.c_str(), "-e", +- (enabled ? "" : "1970-01-02")); +- } +- catch (const InternalFailure& e) +- { +- log<level::ERR>("Unable to modify user enabled state"); +- elog<InternalFailure>(); +- } ++ // Call The User Service to update user groups and priv on the system ++ userSrvc->updateUserStatus(userName, enabled); + + log<level::INFO>("User enabled/disabled state updated successfully", + entry("USER_NAME=%s", userName.c_str()), +@@ -779,54 +620,8 @@ bool UserMgr::userPasswordExpired(const std::string& userName) + + UserSSHLists UserMgr::getUserAndSshGrpList() + { +- // All user management lock has to be based on /etc/shadow +- // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; +- +- std::vector<std::string> userList; +- std::vector<std::string> sshUsersList; +- struct passwd pw, *pwp = nullptr; +- std::array<char, 1024> buffer{}; +- +- phosphor::user::File passwd(passwdFileName, "r"); +- if ((passwd)() == NULL) +- { +- log<level::ERR>("Error opening the passwd file"); +- elog<InternalFailure>(); +- } +- +- while (true) +- { +- auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), buffer.max_size(), +- &pwp); +- if ((r != 0) || (pwp == NULL)) +- { +- // Any error, break the loop. +- break; +- } +-#ifdef ENABLE_ROOT_USER_MGMT +- // Add all users whose UID >= 1000 and < 65534 +- // and special UID 0. +- if ((pwp->pw_uid == 0) || +- ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))) +-#else +- // Add all users whose UID >=1000 and < 65534 +- if ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534)) +-#endif +- { +- std::string userName(pwp->pw_name); +- userList.emplace_back(userName); +- +- // ssh doesn't have separate group. Check login shell entry to +- // get all users list which are member of ssh group. +- std::string loginShell(pwp->pw_shell); +- if (loginShell == "/bin/sh") +- { +- sshUsersList.emplace_back(userName); +- } +- } +- } +- endpwent(); +- return std::make_pair(std::move(userList), std::move(sshUsersList)); ++ // Call The User Service to get the User and SSUsers lists ++ return std::move(userSrvc->getUserAndSshGrpList()); + } + + size_t UserMgr::getIpmiUsersCount() +@@ -837,49 +632,14 @@ size_t UserMgr::getIpmiUsersCount() + + bool UserMgr::isUserEnabled(const std::string& userName) + { +- // All user management lock has to be based on /etc/shadow +- // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; +- std::array<char, 4096> buffer{}; +- struct spwd spwd; +- struct spwd* resultPtr = nullptr; +- int status = getspnam_r(userName.c_str(), &spwd, buffer.data(), +- buffer.max_size(), &resultPtr); +- if (!status && (&spwd == resultPtr)) +- { +- if (resultPtr->sp_expire >= 0) +- { +- return false; // user locked out +- } +- return true; +- } +- return false; // assume user is disabled for any error. ++ // Call The User Service to verify if user is enabled ++ return userSrvc->isUserEnabled(userName); + } + + std::vector<std::string> UserMgr::getUsersInGroup(const std::string& groupName) + { +- std::vector<std::string> usersInGroup; +- // Should be more than enough to get the pwd structure. +- std::array<char, 4096> buffer{}; +- struct group grp; +- struct group* resultPtr = nullptr; +- +- int status = getgrnam_r(groupName.c_str(), &grp, buffer.data(), +- buffer.max_size(), &resultPtr); +- +- if (!status && (&grp == resultPtr)) +- { +- for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) +- { +- usersInGroup.emplace_back(*(grp.gr_mem)); +- } +- } +- else +- { +- log<level::ERR>("Group not found", +- entry("GROUP=%s", groupName.c_str())); +- // Don't throw error, just return empty userList - fallback +- } +- return usersInGroup; ++ // Call The User Service to get the users that belong to a group ++ return std::move(userSrvc->getUsersInGroup(groupName)); + } + + DbusUserObj UserMgr::getPrivilegeMapperObject(void) +@@ -1106,11 +866,9 @@ void UserMgr::initUserObjects(void) + { + // All user management lock has to be based on /etc/shadow + // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; +- std::vector<std::string> userNameList; +- std::vector<std::string> sshGrpUsersList; + UserSSHLists userSSHLists = getUserAndSshGrpList(); +- userNameList = std::move(userSSHLists.first); +- sshGrpUsersList = std::move(userSSHLists.second); ++ std::vector<std::string> userNameList = std::move(userSSHLists.first); ++ std::vector<std::string> sshGrpUsersList = std::move(userSSHLists.second); + + if (!userNameList.empty()) + { +@@ -1165,8 +923,10 @@ void UserMgr::initUserObjects(void) + } + } + +-UserMgr::UserMgr(sdbusplus::bus::bus& bus, const char* path) : +- Ifaces(bus, path, true), bus(bus), path(path) ++UserMgr::UserMgr(sdbusplus::bus::bus& bus, const char* path, ++ UserService::ServiceType srvc) : ++ Ifaces(bus, path, true), ++ bus(bus), path(path) + { + UserMgrIface::allPrivileges(privMgr); + std::sort(groupsMgr.begin(), groupsMgr.end()); +@@ -1274,6 +1034,7 @@ UserMgr::UserMgr(sdbusplus::bus::bus& bus, const char* path) : + } + AccountPolicyIface::accountUnlockTimeout(value32); + } ++ userSrvc = std::make_unique<UserService>(srvc, groupsMgr, privMgr); + initUserObjects(); + + // emit the signal +diff --git a/user_mgr.hpp b/user_mgr.hpp +index f5aac22..5d5ca99 100644 +--- a/user_mgr.hpp ++++ b/user_mgr.hpp +@@ -14,6 +14,7 @@ + // limitations under the License. + */ + #pragma once ++#include "user_service.hpp" + #include "users.hpp" + + #include <sdbusplus/bus.hpp> +@@ -30,8 +31,6 @@ namespace user + { + + using UserMgrIface = sdbusplus::xyz::openbmc_project::User::server::Manager; +-using UserSSHLists = +- std::pair<std::vector<std::string>, std::vector<std::string>>; + using AccountPolicyIface = + sdbusplus::xyz::openbmc_project::User::server::AccountPolicy; + +@@ -77,8 +76,10 @@ class UserMgr : public Ifaces + * + * @param[in] bus - sdbusplus handler + * @param[in] path - D-Bus path ++ * @param[in] srvc - User service to be used + */ +- UserMgr(sdbusplus::bus::bus& bus, const char* path); ++ UserMgr(sdbusplus::bus::bus& bus, const char* path, ++ UserService::ServiceType srvc); + + /** @brief create user method. + * This method creates a new user as requested +@@ -194,6 +195,8 @@ class UserMgr : public Ifaces + /** @brief object path */ + const std::string path; + ++ /** @brief user service to be used */ ++ std::unique_ptr<UserService> userSrvc; + /** @brief privilege manager container */ + std::vector<std::string> privMgr = {"priv-admin", "priv-operator", + "priv-user", "priv-noaccess"}; +diff --git a/user_service.cpp b/user_service.cpp +new file mode 100644 +index 0000000..6e11755 +--- /dev/null ++++ b/user_service.cpp +@@ -0,0 +1,789 @@ ++/* ++// Copyright (c) 2018 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++ ++#include "user_service.hpp" ++ ++#include "file.hpp" ++#include "shadowlock.hpp" ++ ++#include <grp.h> ++#include <pwd.h> ++ ++#include <boost/algorithm/string/split.hpp> ++#include <boost/process/child.hpp> ++#include <boost/process/io.hpp> ++ ++#include <numeric> ++ ++/* anonymous namespace for User Service interface implementations. ++// Each class inside this namespace implements a special service ++// to be used for the User Manager class. This can be extended to use ++// other user management services and it should be as simple as ++// adding a new class which inherits from phosphor::user::UserServiceInterface ++*/ ++ ++namespace ++{ ++ ++std::string getCSVFromVector(std::vector<std::string> vec) ++{ ++ switch (vec.size()) ++ { ++ case 0: ++ { ++ return ""; ++ } ++ break; ++ ++ case 1: ++ { ++ return std::string{vec[0]}; ++ } ++ break; ++ ++ default: ++ { ++ return std::accumulate( ++ std::next(vec.begin()), vec.end(), vec[0], ++ [](std::string a, std::string b) { return a + ',' + b; }); ++ } ++ } ++} ++ ++bool removeStringFromCSV(std::string& csvStr, const std::string& delStr) ++{ ++ std::string::size_type delStrPos = csvStr.find(delStr); ++ if (delStrPos != std::string::npos) ++ { ++ // need to also delete the comma char ++ if (delStrPos == 0) ++ { ++ csvStr.erase(delStrPos, delStr.size() + 1); ++ } ++ else ++ { ++ csvStr.erase(delStrPos - 1, delStr.size() + 1); ++ } ++ return true; ++ } ++ return false; ++} ++ ++class ShadowService : public phosphor::user::UserServiceInterface ++{ ++ public: ++ ShadowService() = default; ++ ++ ~ShadowService() = default; ++ ++ phosphor::user::UserSSHLists getUserAndSshGrpList() const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; ++ ++ std::vector<std::string> userList; ++ std::vector<std::string> sshUsersList; ++ ++ struct passwd pw, *pwp = nullptr; ++ std::array<char, 1024> buffer{}; ++ ++ phosphor::user::File passwd(passwdFileName, "r"); ++ if ((passwd)() == NULL) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error opening the passwd file"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ while (true) ++ { ++ auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), ++ buffer.max_size(), &pwp); ++ if ((r != 0) || (pwp == NULL)) ++ { ++ // Any error, break the loop. ++ break; ++ } ++#ifdef ENABLE_ROOT_USER_MGMT ++ // Add all users whose UID >= 1000 and < 65534 ++ // and special UID 0. ++ if ((pwp->pw_uid == 0) || ++ ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))) ++#else ++ // Add all users whose UID >=1000 and < 65534 ++ if ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534)) ++#endif ++ { ++ std::string userName(pwp->pw_name); ++ userList.emplace_back(userName); ++ ++ // ssh doesn't have separate group. Check login shell entry to ++ // get all users list which are member of ssh group. ++ std::string loginShell(pwp->pw_shell); ++ if (loginShell == "/bin/sh") ++ { ++ sshUsersList.emplace_back(userName); ++ } ++ } ++ } ++ endpwent(); ++ return std::make_pair(std::move(userList), std::move(sshUsersList)); ++ } ++ ++ std::vector<std::string> ++ getUsersInGroup(const std::string& groupName) const override ++ { ++ std::vector<std::string> usersInGroup; ++ // Should be more than enough to get the pwd structure. ++ std::array<char, 4096> buffer{}; ++ struct group grp; ++ struct group* grpPtr = &grp; ++ struct group* resultPtr; ++ ++ int status = getgrnam_r(groupName.c_str(), grpPtr, buffer.data(), ++ buffer.max_size(), &resultPtr); ++ ++ if (!status && (grpPtr == resultPtr)) ++ { ++ for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) ++ { ++ usersInGroup.emplace_back(*(grp.gr_mem)); ++ } ++ } ++ else ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Group not found", ++ phosphor::logging::entry("GROUP=%s", groupName.c_str())); ++ // Don't throw error, just return empty usersInGroup - fallback ++ } ++ return usersInGroup; ++ } ++ ++ void createUser(const std::string& userName, ++ const std::vector<std::string>& groupNames, ++ const std::string& priv, const bool& enabled) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; ++ ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups.append(","); ++ } ++ groups.append(priv); ++ } ++ ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(), ++ "-m", "-N", "-s", (sshRequested ? "/bin/sh" : "/bin/nologin"), ++ "-e", (enabled ? "" : "1970-01-02")); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to create new user"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void renameUser(const std::string& userName, ++ const std::string& newUserName) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; ++ try ++ { ++ std::string newHomeDir = "/home/" + newUserName; ++ phosphor::user::executeCmd("/usr/sbin/usermod", "-l", ++ newUserName.c_str(), userName.c_str(), ++ "-d", newHomeDir.c_str(), "-m"); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "User rename failed", ++ phosphor::logging::entry("USER_NAME=%s", userName.c_str())); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void deleteUser(const std::string& userName) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; ++ ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/userdel", userName.c_str(), ++ "-r"); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "User delete failed", ++ phosphor::logging::entry("USER_NAME=%s", userName.c_str())); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateGroupsAndPriv(const std::string& userName, ++ const std::vector<std::string>& groupNames, ++ const std::string& priv) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; ++ ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same. ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups += ","; ++ } ++ groups += priv; ++ } ++ ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/usermod", userName.c_str(), "-G", groups.c_str(), ++ "-s", (sshRequested ? "/bin/sh" : "/bin/nologin")); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to modify user privilege / groups"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateUserStatus(const std::string& userName, ++ const bool& enabled) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/usermod", userName.c_str(), ++ "-e", (enabled ? "" : "1970-01-02")); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to modify user enabled state"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ bool isUserEnabled(const std::string& userName) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ // TODO phosphor-user-manager#10 phosphor::user::shadow::Lock lock{}; ++ std::array<char, 4096> buffer{}; ++ struct spwd spwd; ++ struct spwd* resultPtr = nullptr; ++ int status = getspnam_r(userName.c_str(), &spwd, buffer.data(), ++ buffer.max_size(), &resultPtr); ++ if (!status && (&spwd == resultPtr)) ++ { ++ if (resultPtr->sp_expire >= 0) ++ { ++ return false; // user locked out ++ } ++ return true; ++ } ++ return false; // assume user is disabled for any error. ++ } ++ ++ std::vector<std::string> ++ getUserGroups(const std::string& userName) const override ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "ShadowService::getUserGroups not implemented!"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ return std::vector<std::string>(); ++ } ++ ++ void createGroup(const std::string& groupName) const override ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "ShadowService::createGroup not implemented!"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ private: ++ static constexpr const char* passwdFileName = "/etc/passwd"; ++}; ++ ++class SSSDService : public phosphor::user::UserServiceInterface ++{ ++ public: ++ SSSDService(const std::vector<std::string>& groups, ++ const std::vector<std::string>& privs) ++ { ++ ++ createGroup(lockedGrp); ++ for (const auto& g : groups) ++ { ++ createGroup(g); ++ } ++ for (const auto& p : privs) ++ { ++ createGroup(p); ++ } ++ } ++ ++ ~SSSDService() = default; ++ ++ phosphor::user::UserSSHLists getUserAndSshGrpList() const override ++ { ++ std::vector<std::string> users; ++ std::vector<std::string> sshGroup; ++ std::vector<std::string> exeOutput; ++ ++ try ++ { ++ exeOutput = phosphor::user::executeCmd("/usr/bin/getent", "-s", ++ "sss", "passwd"); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get users information " ++ "from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ for (const auto& userLine : exeOutput) ++ { ++ std::vector<std::string> userInfo; ++ boost::algorithm::split(userInfo, userLine, ++ boost::algorithm::is_any_of(":")); ++ // At this point userInfo is a vector containing the passwd ++ // info for the user, so we know the correct positions: ++ // 0: User name. ++ // 1: Encrypted password. ++ // 2: User ID number (UID) ++ // 3: User's group ID number (GID) ++ // 4: Full name of the user (GECOS) ++ // 5: User home directory. ++ // 6: Login shell. ++ users.emplace_back(userInfo[0]); ++ ++ // ssh doesn't have separate group. Check login shell entry to ++ // get all users list which are member of ssh group. ++ if (userInfo[6] == "/bin/sh") ++ { ++ sshGroup.emplace_back(userInfo[0]); ++ } ++ } ++ ++ return std::make_pair(std::move(users), std::move(sshGroup)); ++ } ++ ++ std::vector<std::string> ++ getUsersInGroup(const std::string& groupName) const override ++ { ++ std::vector<std::string> userList; ++ std::vector<std::string> exeOutput; ++ ++ try ++ { ++ exeOutput = phosphor::user::executeCmd("/usr/sbin/sss_groupshow", ++ groupName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get group users from sssd service"); ++ // Don't throw error, just return empty usersInGroup - return ++ return userList; ++ } ++ // exeOutput should have 5 entries ++ // 0: Group ++ // 1: GID number ++ // 2: Member users ++ // 3: Is a member of ++ // 4: Member groups ++ exeOutput[2].erase( ++ exeOutput[2].begin(), ++ std::find(exeOutput[2].begin(), exeOutput[2].end(), ':')); ++ boost::algorithm::trim_left(exeOutput[2]); ++ boost::algorithm::split(userList, exeOutput[2], ++ boost::algorithm::is_any_of(",")); ++ return userList; ++ } ++ ++ void createUser(const std::string& userName, ++ const std::vector<std::string>& groupNames, ++ const std::string& priv, const bool& enabled) const override ++ { ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups += ","; ++ } ++ groups += priv; ++ } ++ ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/sss_useradd", "-m", "-G", groups.c_str(), "-s", ++ (sshRequested ? "/bin/sh" : "/bin/nologin"), userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to create new user in sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ // Sometimes the SSSD service needs some time to actually ++ // reflect the changes to the local DB to the NSS service, ++ // that is why we have this sleep here ... ++ std::this_thread::sleep_for(std::chrono::seconds(1)); ++ // update user status (locked/unlocked) ++ updateUserStatus(userName, enabled); ++ } ++ ++ void renameUser(const std::string& userName, ++ const std::string& newUserName) const override ++ { ++ std::vector<std::string> exeOutput; ++ // Local Domain for sssd doesn't have a rename feature ++ // so we need to first create a new user and then delete ++ // the old one. ++ // The only issue with this is that the password for the ++ // user will have to be reseted since it is a new user being created. ++ ++ // Get original user groups ++ std::vector<std::string> groups = getUserGroups(userName); ++ // Check if it has a "ssh" group by looking for the shell login ++ try ++ { ++ exeOutput = phosphor::user::executeCmd( ++ "/usr/bin/getent", "-s", "sss", "passwd", userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get information for user"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ if (exeOutput[0].find("/bin/sh")) ++ { ++ groups.emplace_back(phosphor::user::grpSsh); ++ } ++ // Call create user with the new user names and previous groups ++ // Priv is already part of the groups so that can be empty. ++ createUser(newUserName, groups, "", isUserEnabled(userName)); ++ ++ // Now delete original user ++ deleteUser(userName); ++ } ++ ++ void deleteUser(const std::string& userName) const override ++ { ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/sss_userdel", "-r", ++ userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to delete user from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateGroupsAndPriv(const std::string& userName, ++ const std::vector<std::string>& groupNames, ++ const std::string& priv) const override ++ { ++ // local domain sssd do not allow to update all list of groups, ++ // so we will remove all groups first (except for the user one) ++ // and then all all the ones that were passed ++ std::string oldGroups = getCSVFromVector(getUserGroups(userName)); ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups += ","; ++ } ++ groups += priv; ++ } ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/sss_usermod", "-r", oldGroups.c_str(), "-a", ++ groups.c_str(), "-s", ++ (sshRequested ? "/bin/sh" : "/bin/nologin"), userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to update user groups and " ++ "priv from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateUserStatus(const std::string& userName, ++ const bool& enabled) const override ++ { ++ std::string enabledStr; ++ std::string lockedStr; ++ if (isUserEnabled(userName) == enabled) ++ { ++ return; ++ } ++ if (enabled) ++ { ++ enabledStr = "-r"; ++ lockedStr = "-U"; ++ } ++ else ++ { ++ enabledStr = "-a"; ++ lockedStr = "-L"; ++ } ++ try ++ { ++ // We will add a special locked group to identify the users ++ // that have been locked out of the system. ++ // TODO: sss_usermod is not locking user accounts for the ++ // LOCAL domain, need to find the correct PAM configuration ++ // to actually lockout users for SSSD. ++ // As a workaround we are using the pam module pam_listfile.so ++ // to lockout all users that belong to the locked group. ++ phosphor::user::executeCmd("/usr/sbin/sss_usermod", ++ enabledStr.c_str(), lockedGrp.c_str(), ++ lockedStr.c_str(), userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to update user status from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ bool isUserEnabled(const std::string& userName) const override ++ { ++ std::vector<std::string> userGrps = getUserGroups(userName); ++ return std::find(userGrps.begin(), userGrps.end(), lockedGrp) == ++ userGrps.end(); ++ } ++ ++ std::vector<std::string> ++ getUserGroups(const std::string& userName) const override ++ { ++ std::vector<std::string> exeOutput; ++ try ++ { ++ exeOutput = ++ phosphor::user::executeCmd("/usr/bin/groups", userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get groups for user"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ std::vector<std::string> groups; ++ boost::algorithm::split(groups, exeOutput[0], ++ boost::algorithm::is_any_of(" ")); ++ // Delete group that equals user name if it exists ++ auto userNameGroup = std::find(groups.begin(), groups.end(), userName); ++ if (userNameGroup != groups.end()) ++ { ++ groups.erase(userNameGroup); ++ } ++ return groups; ++ } ++ ++ void createGroup(const std::string& groupName) const override ++ { ++ try ++ { ++ if (!groupExists(groupName)) ++ { ++ phosphor::user::executeCmd("/usr/sbin/sss_groupadd", ++ groupName.c_str()); ++ } ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to create group"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ private: ++ static const std::string lockedGrp; ++ ++ bool groupExists(const std::string& groupName) const ++ { ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/sss_groupshow", ++ groupName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure& e) ++ { ++ return false; ++ } ++ return true; ++ } ++}; ++ ++const std::string SSSDService::lockedGrp = "sssd_locked"; ++} // anonymous namespace ++ ++namespace phosphor ++{ ++namespace user ++{ ++ ++UserService::UserService(const ServiceType& srvcType, ++ const std::vector<std::string>& groups, ++ const std::vector<std::string>& privs) ++{ ++ setServiceImpl(srvcType, groups, privs); ++} ++ ++void UserService::updateServiceType(const ServiceType& srvcType, ++ const std::vector<std::string>& groups, ++ const std::vector<std::string>& privs) ++{ ++ usrSrvcImpl.reset(); ++ setServiceImpl(srvcType, groups, privs); ++} ++ ++void UserService::setServiceImpl(const ServiceType& srvcType, ++ const std::vector<std::string>& groups, ++ const std::vector<std::string>& privs) ++{ ++ switch (srvcType) ++ { ++ case ServiceType::shadow: ++ { ++ usrSrvcImpl = std::make_unique<ShadowService>(); ++ } ++ break; ++ ++ case ServiceType::sssd: ++ { ++ usrSrvcImpl = std::make_unique<SSSDService>(groups, privs); ++ } ++ break; ++ ++ case ServiceType::none: ++ default: ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Invalid service type initialization!"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ break; ++ } ++} ++ ++UserService::~UserService() ++{} ++ ++phosphor::user::UserSSHLists UserService::getUserAndSshGrpList() const ++{ ++ return usrSrvcImpl->getUserAndSshGrpList(); ++} ++ ++std::vector<std::string> ++ UserService::getUsersInGroup(const std::string& groupName) const ++{ ++ return usrSrvcImpl->getUsersInGroup(groupName); ++} ++ ++void UserService::createUser(const std::string& userName, ++ const std::vector<std::string>& groupNames, ++ const std::string& priv, const bool& enabled) const ++{ ++ usrSrvcImpl->createUser(userName, groupNames, priv, enabled); ++} ++ ++void UserService::renameUser(const std::string& userName, ++ const std::string& newUserName) const ++{ ++ usrSrvcImpl->renameUser(userName, newUserName); ++} ++ ++void UserService::deleteUser(const std::string& userName) const ++{ ++ usrSrvcImpl->deleteUser(userName); ++} ++ ++void UserService::updateGroupsAndPriv( ++ const std::string& userName, const std::vector<std::string>& groupNames, ++ const std::string& priv) const ++{ ++ usrSrvcImpl->updateGroupsAndPriv(userName, groupNames, priv); ++} ++ ++void UserService::updateUserStatus(const std::string& userName, ++ const bool& enabled) const ++{ ++ usrSrvcImpl->updateUserStatus(userName, enabled); ++} ++ ++bool UserService::isUserEnabled(const std::string& userName) const ++{ ++ return usrSrvcImpl->isUserEnabled(userName); ++} ++ ++std::vector<std::string> ++ UserService::getUserGroups(const std::string& userName) const ++{ ++ return usrSrvcImpl->getUserGroups(userName); ++} ++ ++} // namespace user ++} // namespace phosphor +diff --git a/user_service.hpp b/user_service.hpp +new file mode 100644 +index 0000000..50ee4db +--- /dev/null ++++ b/user_service.hpp +@@ -0,0 +1,233 @@ ++/* ++// Copyright (c) 2018 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++ ++#pragma once ++#include <boost/process/child.hpp> ++#include <boost/process/io.hpp> ++#include <phosphor-logging/elog.hpp> ++#include <phosphor-logging/log.hpp> ++#include <xyz/openbmc_project/Common/error.hpp> ++#include <xyz/openbmc_project/User/Common/error.hpp> ++ ++namespace phosphor ++{ ++namespace user ++{ ++ ++using UserSSHLists = ++ std::pair<std::vector<std::string>, std::vector<std::string>>; ++using InternalFailure = ++ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; ++using InsufficientPermission = ++ sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission; ++ ++const std::string grpSsh = "ssh"; ++ ++template <typename... ArgTypes> ++std::vector<std::string> executeCmd(const char* path, ArgTypes&&... tArgs) ++{ ++ std::vector<std::string> stdOutput; ++ boost::process::ipstream stdOutStream; ++ boost::process::child execProg(path, const_cast<char*>(tArgs)..., ++ boost::process::std_out > stdOutStream); ++ std::string stdOutLine; ++ ++ while (stdOutStream && std::getline(stdOutStream, stdOutLine) && ++ !stdOutLine.empty()) ++ { ++ stdOutput.emplace_back(stdOutLine); ++ } ++ ++ execProg.wait(); ++ ++ int retCode = execProg.exit_code(); ++ if (retCode) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Command execution failed", ++ phosphor::logging::entry("PATH=%d", path), ++ phosphor::logging::entry("RETURN_CODE:%d", retCode)); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ return stdOutput; ++} ++ ++/** @class UserServiceInterface ++ * @brief Interface class for methods provided by the implemmentations ++ * of the user service. Provides the same methods as the UserService ++ * class. ++ */ ++class UserServiceInterface ++{ ++ public: ++ UserServiceInterface() = default; ++ virtual ~UserServiceInterface() = default; ++ virtual UserSSHLists getUserAndSshGrpList() const = 0; ++ virtual std::vector<std::string> ++ getUsersInGroup(const std::string& groupName) const = 0; ++ virtual void createUser(const std::string& userName, ++ const std::vector<std::string>& groupNames, ++ const std::string& priv, ++ const bool& enabled) const = 0; ++ virtual void renameUser(const std::string& userName, ++ const std::string& newUserName) const = 0; ++ virtual void deleteUser(const std::string& userName) const = 0; ++ virtual void updateGroupsAndPriv(const std::string& userName, ++ const std::vector<std::string>& groupNames, ++ const std::string& priv) const = 0; ++ virtual void updateUserStatus(const std::string& userName, ++ const bool& enabled) const = 0; ++ virtual bool isUserEnabled(const std::string& userName) const = 0; ++ virtual std::vector<std::string> ++ getUserGroups(const std::string& userName) const = 0; ++ virtual void createGroup(const std::string& groupName) const = 0; ++}; ++ ++/** @class UserService ++ * @brief Responsible for managing the user service for the user manager. ++ * This service is the one responsible to actually change the user information ++ * of the application. It can support sevaral services, currently the ones ++ * supported are: ++ * ++ * 1) Shadow: Which uses the /etc/shadow file for updating the users ++ * 2) SSSD: Which uses the sssd service for a LOCAL domain only right now. ++ */ ++class UserService ++{ ++ public: ++ UserService() = delete; ++ UserService(const UserService&) = delete; ++ UserService& operator=(const UserService&) = delete; ++ UserService(UserService&&) = delete; ++ UserService& operator=(UserService&&) = delete; ++ ++ // Service Types implemented. None is used to validate. ++ enum class ServiceType ++ { ++ none, ++ shadow, ++ sssd ++ }; ++ ++ UserService(const ServiceType& srvcType, ++ const std::vector<std::string>& groups, ++ const std::vector<std::string>& privs); ++ ~UserService(); ++ ++ /** @brief update the current Service type of the instance. ++ * This function is used to update in real time the service ++ * being used for the user management without restarting the ++ * whole service. ++ * ++ * @param[in] srvcType ++ * @param[in] groups ++ * @param[in] privs ++ */ ++ void updateServiceType(const ServiceType& srvcType, ++ const std::vector<std::string>& groups, ++ const std::vector<std::string>& privs); ++ ++ /** @brief get user list and SSH group members list ++ * This method gets the list of users from the service. ++ * If the userlist reference is empty, all the users will be added ++ * and DBus notified about them. If the list is not empty, the function ++ * will only update list adding the missing ones to it. It will not remove ++ * any extra users on the list that are not part of the service! ++ * ++ */ ++ UserSSHLists getUserAndSshGrpList() const; ++ ++ /** @brief Get users in group. ++ * This method creates a new user as requested ++ * ++ * @param[in] groupName - Name of the group which has to be queried ++ */ ++ std::vector<std::string> ++ getUsersInGroup(const std::string& groupName) const; ++ ++ /** @brief create user method. ++ * This method creates a new user as requested ++ * ++ * @param[in] userName - Name of the user which has to be created ++ * @param[in] groupNames - Group names list, to which user has to be added. ++ * @param[in] priv - Privilege of the user. ++ * @param[in] enabled - State of the user enabled / disabled. ++ */ ++ void createUser(const std::string& userName, ++ const std::vector<std::string>& groupNames, ++ const std::string& priv, const bool& enabled) const; ++ ++ /** @brief rename user method. ++ * This method renames the user as requested ++ * ++ * @param[in] userName - current name of the user ++ * @param[in] userName - user name to which it has to be renamed. ++ */ ++ void renameUser(const std::string& userName, ++ const std::string& newUserName) const; ++ ++ /** @brief delete user method. ++ * This method deletes the user as requested ++ * ++ * @param[in] userName - Name of the user which has to be deleted ++ */ ++ void deleteUser(const std::string& userName) const; ++ ++ /** @brief Updates user Groups and Privilege. ++ * ++ * @param[in] userName - Name of the user which has to be modified ++ * @param[in] groupNames - Group names list for user. ++ * @param[in] priv - Privilege of the user. ++ */ ++ void updateGroupsAndPriv(const std::string& userName, ++ const std::vector<std::string>& groupNames, ++ const std::string& priv) const; ++ ++ /** @brief Updates user status ++ * If enabled = false: User will be disabled ++ * If enabled = true : User will be enabled ++ * ++ * @param[in] userName - Name of the user ++ * @param[in] enabled - Status of the user: enabled / disabled? ++ */ ++ void updateUserStatus(const std::string& userName, ++ const bool& enabled) const; ++ ++ /** @brief Verify if user is enabled or not ++ * If enabled returns true ++ * If not enabled returns false ++ * ++ * @param[in] userName - Name of the user ++ */ ++ bool isUserEnabled(const std::string& userName) const; ++ ++ /** @brief Get the list of groups a user belongs to ++ * ++ * @param[in] userName - Name of the user ++ */ ++ std::vector<std::string> getUserGroups(const std::string& userName) const; ++ ++ private: ++ // User service implementation. ++ void setServiceImpl(const ServiceType& srvcType, ++ const std::vector<std::string>& groups, ++ const std::vector<std::string>& privs); ++ std::unique_ptr<UserServiceInterface> usrSrvcImpl; ++}; ++ ++} // namespace user ++} // namespace phosphor +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0006-Use-groupmems-instead-of-getgrnam_r-due-to-overlay.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0006-Use-groupmems-instead-of-getgrnam_r-due-to-overlay.patch new file mode 100644 index 000000000..7a0eff80e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0006-Use-groupmems-instead-of-getgrnam_r-due-to-overlay.patch @@ -0,0 +1,76 @@ +From 06064b3d6e56f4e13e6b85552f8525b74d9f1931 Mon Sep 17 00:00:00 2001 +From: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +Date: Mon, 24 Feb 2020 13:37:12 +0530 +Subject: [PATCH 2/2] Use groupmems instead of getgrnam_r due to overlay + +With JFFS2 overlay, getgrnam_r during initial time returns the +old group details as per the lower dir, instead of the overlay one +but at the same time groupmems where returning proper values, which +reads the file everytime. Hence replacing getgrnam_r with groupmems + +Tested: +1. Verified that when added multiple user and then doing +BMC reset using ipmitool raw 6 2 doesn't reproduce the issue of +user with only ssh group. (on 38 version source + this fix) +2. Updated using redfish to version 39 + this fix, and made sure +issue doesn't happen. + +Note: For testing purpose added debug statements to dump ouput of +both getgrnam_r & groupmems and able to see proper list only +in groupmems when the issue is reproduced + +Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +Signed-off-by: jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +--- + user_service.cpp | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) + +diff --git a/user_service.cpp b/user_service.cpp +index ad4e510..89b27ed 100644 +--- a/user_service.cpp ++++ b/user_service.cpp +@@ -147,28 +147,26 @@ class ShadowService : public phosphor::user::UserServiceInterface + getUsersInGroup(const std::string& groupName) const override + { + std::vector<std::string> usersInGroup; +- // Should be more than enough to get the pwd structure. +- std::array<char, 4096> buffer{}; +- struct group grp; +- struct group* grpPtr = &grp; +- struct group* resultPtr; +- +- int status = getgrnam_r(groupName.c_str(), grpPtr, buffer.data(), +- buffer.max_size(), &resultPtr); +- +- if (!status && (grpPtr == resultPtr)) ++ std::vector<std::string> output; ++ try + { +- for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) +- { +- usersInGroup.emplace_back(*(grp.gr_mem)); +- } ++ output = phosphor::user::executeCmd("/usr/sbin/groupmems", "-l", ++ "-g", groupName.c_str()); + } +- else ++ catch (const phosphor::user::InternalFailure& e) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Group not found", + phosphor::logging::entry("GROUP=%s", groupName.c_str())); + // Don't throw error, just return empty usersInGroup - fallback ++ return usersInGroup; ++ } ++ if (!output.empty()) ++ { ++ boost::algorithm::trim_right(output[0]); ++ boost::algorithm::split(usersInGroup, output[0], ++ boost::algorithm::is_any_of("\t "), ++ boost::token_compress_on); + } + return usersInGroup; + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend new file mode 100644 index 000000000..21c7991bd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend @@ -0,0 +1,16 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI = "git://github.com/openbmc/phosphor-user-manager" +SRCREV = "c0760c9109a0d847fd77d54c6b7948322a375d1d" + +EXTRA_OECONF += "${@bb.utils.contains_any("IMAGE_FEATURES", [ 'debug-tweaks', 'allow-root-login' ], '', '--disable-root_user_mgmt', d)}" + +SRC_URI += " \ + file://0005-Added-suport-for-multiple-user-manager-services.patch \ + file://0006-Use-groupmems-instead-of-getgrnam_r-due-to-overlay.patch \ + " + +FILES:${PN} += "${datadir}/dbus-1/system.d/phosphor-nslcd-cert-config.conf" +FILES:${PN} += "/usr/share/phosphor-certificate-manager/nslcd" +FILES:${PN} += "\ + /lib/systemd/system/multi-user.target.wants/phosphor-certificate-manager@nslcd.service" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/virtual-media/virtual-media.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/virtual-media/virtual-media.bb new file mode 100644 index 000000000..b8aedc6ee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/virtual-media/virtual-media.bb @@ -0,0 +1,24 @@ +SUMMARY = "Virtual Media Service" +DESCRIPTION = "Virtual Media Service" + +SRC_URI = "git://github.com/Intel-BMC/provingground.git;protocol=ssh" +SRCREV = "bee56d62b209088454d166d1efae4825a2b175df" + +S = "${WORKDIR}/git/virtual-media" +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SYSTEMD_SERVICE:${PN} += "xyz.openbmc_project.VirtualMedia.service" + +DEPENDS = "udev boost nlohmann-json systemd sdbusplus" + +RDEPENDS:${PN} = "nbd-client nbdkit" + +inherit cmake systemd + +EXTRA_OECMAKE += "-DYOCTO_DEPENDENCIES=ON" +EXTRA_OECMAKE += "-DLEGACY_MODE_ENABLED=ON" + +FULL_OPTIMIZATION = "-Os -pipe -flto -fno-rtti" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch new file mode 100644 index 000000000..48bc4a086 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch @@ -0,0 +1,348 @@ +From a7d1d3770a650df8fe61d594885fcb388ac2ca42 Mon Sep 17 00:00:00 2001 +From: James Feist <james.feist@linux.intel.com> +Date: Mon, 17 Jun 2019 12:00:58 -0700 +Subject: [PATCH] Customize phosphor-watchdog for Intel platforms + +This patch adds various changes to phosphor-watchdog that are +required for compatibility with Intel platforms. + + 1. Add Redfish messages for watchdog timeout and pre-interrupt + 2. Use dbus properties for power control insted of service files + 3. Use host status to enable/disable watchdog + 4. Set preTimeoutInterruptOccurFlag + 5. Assign watchdog cause for correct reset cause reporting + 6. Add NMI Pre-Interrupt support for IPMI watchdog timer. + +Signed-off-by: James Feist <james.feist@linux.intel.com> +Signed-off-by: Ren Yu <yux.ren@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com> +Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com> +Signed-off-by: Sunita Kumari <sunitax.kumari@intel.com> + +%% original patch: 0001-Customize-phosphor-watchdog-for-Intel-platforms.patch +--- + src/watchdog.cpp | 229 ++++++++++++++++++++++++++++++++++++++++++++--- + src/watchdog.hpp | 22 ++++- + 2 files changed, 240 insertions(+), 11 deletions(-) + +diff --git a/src/watchdog.cpp b/src/watchdog.cpp +index 7de98ae3e70f..f96faebc7368 100644 +--- a/src/watchdog.cpp ++++ b/src/watchdog.cpp +@@ -5,8 +5,10 @@ + #include <phosphor-logging/elog.hpp> + #include <phosphor-logging/log.hpp> + #include <sdbusplus/exception.hpp> ++#include <systemd/sd-journal.h> + #include <string_view> + #include <xyz/openbmc_project/Common/error.hpp> ++#include <xyz/openbmc_project/State/Host/server.hpp> + + namespace phosphor + { +@@ -19,10 +21,86 @@ using namespace phosphor::logging; + using sdbusplus::exception::SdBusError; + using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; + +-// systemd service to kick start a target. +-constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; +-constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1"; +-constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; ++const static constexpr char* currentHostState = "CurrentHostState"; ++const static constexpr char* hostStatusOff = ++ "xyz.openbmc_project.State.Host.HostState.Off"; ++ ++const static constexpr char* actionDescription = " due to Watchdog timeout"; ++const static constexpr char* hardResetDescription = "Hard Reset - System reset"; ++const static constexpr char* powerOffDescription = ++ "Power Down - System power down"; ++const static constexpr char* powerCycleDescription = ++ "Power Cycle - System power cycle"; ++const static constexpr char* timerExpiredDescription = "Timer expired"; ++ ++const static constexpr char* preInterruptActionNone = ++ "xyz.openbmc_project.State.Watchdog.PreTimeoutInterruptAction.None"; ++ ++const static constexpr char* preInterruptDescriptionSMI = "SMI"; ++const static constexpr char* preInterruptDescriptionNMI = "NMI"; ++const static constexpr char* preInterruptDescriptionMI = "Messaging Interrupt"; ++ ++const static constexpr char* reservedDescription = "Reserved"; ++ ++const static constexpr char* timerUseDescriptionBIOSFRB2 = "BIOS FRB2"; ++const static constexpr char* timerUseDescriptionBIOSPOST = "BIOS/POST"; ++const static constexpr char* timerUseDescriptionOSLoad = "OSLoad"; ++const static constexpr char* timerUseDescriptionSMSOS = "SMS/OS"; ++const static constexpr char* timerUseDescriptionOEM = "OEM"; ++ ++namespace restart ++{ ++static constexpr const char* busName = ++ "xyz.openbmc_project.Control.Host.RestartCause"; ++static constexpr const char* path = ++ "/xyz/openbmc_project/control/host0/restart_cause"; ++static constexpr const char* interface = ++ "xyz.openbmc_project.Control.Host.RestartCause"; ++static constexpr const char* property = "RequestedRestartCause"; ++} // namespace restart ++ ++// chassis state manager service ++namespace chassis ++{ ++static constexpr const char* busName = "xyz.openbmc_project.State.Chassis"; ++static constexpr const char* path = "/xyz/openbmc_project/state/chassis0"; ++static constexpr const char* interface = "xyz.openbmc_project.State.Chassis"; ++static constexpr const char* request = "RequestedPowerTransition"; ++} // namespace chassis ++ ++namespace host ++{ ++static constexpr const char* busName = "xyz.openbmc_project.State.Host"; ++static constexpr const char* path = "/xyz/openbmc_project/state/host0"; ++static constexpr const char* interface = "xyz.openbmc_project.State.Host"; ++static constexpr const char* request = "RequestedHostTransition"; ++} // namespace host ++ ++namespace nmi ++{ ++static constexpr const char* busName = "xyz.openbmc_project.Control.Host.NMI"; ++static constexpr const char* path = "/xyz/openbmc_project/control/host0/nmi"; ++static constexpr const char* interface = "xyz.openbmc_project.Control.Host.NMI"; ++static constexpr const char* request = "NMI"; ++ ++} // namespace nmi ++ ++void Watchdog::powerStateChangedHandler( ++ const std::map<std::string, std::variant<std::string>>& props) ++{ ++ const auto iter = props.find(currentHostState); ++ if (iter != props.end()) ++ { ++ const std::string* powerState = std::get_if<std::string>(&iter->second); ++ if (powerState && (*powerState == hostStatusOff)) ++ { ++ if (timerEnabled()) ++ { ++ enabled(false); ++ } ++ } ++ } ++} + + void Watchdog::resetTimeRemaining(bool enableWatchdog) + { +@@ -108,13 +186,111 @@ uint64_t Watchdog::interval(uint64_t value) + // Optional callback function on timer expiration + void Watchdog::timeOutHandler() + { ++ PreTimeoutInterruptAction preTimeoutInterruptAction = preTimeoutInterrupt(); ++ std::string preInterruptActionMessageArgs{}; ++ + Action action = expireAction(); ++ std::string actionMessageArgs{}; ++ ++ expiredTimerUse(currentTimerUse()); ++ ++ TimerUse timeUser = expiredTimerUse(); ++ std::string timeUserMessage{}; ++ + if (!this->enabled()) + { + action = fallback->action; + } + +- expiredTimerUse(currentTimerUse()); ++ switch (timeUser) ++ { ++ case Watchdog::TimerUse::BIOSFRB2: ++ timeUserMessage = timerUseDescriptionBIOSFRB2; ++ break; ++ case Watchdog::TimerUse::BIOSPOST: ++ timeUserMessage = timerUseDescriptionBIOSPOST; ++ break; ++ case Watchdog::TimerUse::OSLoad: ++ timeUserMessage = timerUseDescriptionOSLoad; ++ break; ++ case Watchdog::TimerUse::SMSOS: ++ timeUserMessage = timerUseDescriptionSMSOS; ++ break; ++ case Watchdog::TimerUse::OEM: ++ timeUserMessage = timerUseDescriptionOEM; ++ break; ++ default: ++ timeUserMessage = reservedDescription; ++ break; ++ } ++ ++ switch (action) ++ { ++ case Watchdog::Action::HardReset: ++ actionMessageArgs = std::string(hardResetDescription) + ++ std::string(actionDescription); ++ break; ++ case Watchdog::Action::PowerOff: ++ actionMessageArgs = std::string(powerOffDescription) + ++ std::string(actionDescription); ++ break; ++ case Watchdog::Action::PowerCycle: ++ actionMessageArgs = std::string(powerCycleDescription) + ++ std::string(actionDescription); ++ break; ++ case Watchdog::Action::None: ++ actionMessageArgs = timerExpiredDescription; ++ break; ++ default: ++ actionMessageArgs = reservedDescription; ++ break; ++ } ++ ++ // Log into redfish event log ++ sd_journal_send("MESSAGE=IPMIWatchdog: Timed out ACTION=%s", ++ convertForMessage(action).c_str(), "PRIORITY=%i", LOG_INFO, ++ "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.IPMIWatchdog", ++ "REDFISH_MESSAGE_ARGS=%s. timer use: %s", ++ actionMessageArgs.c_str(), timeUserMessage.c_str(), NULL); ++ ++ switch (preTimeoutInterruptAction) ++ { ++ case Watchdog::PreTimeoutInterruptAction::SMI: ++ preInterruptActionMessageArgs = preInterruptDescriptionSMI; ++ break; ++ case Watchdog::PreTimeoutInterruptAction::NMI: ++ preInterruptActionMessageArgs = preInterruptDescriptionNMI; ++ break; ++ case Watchdog::PreTimeoutInterruptAction::MI: ++ preInterruptActionMessageArgs = preInterruptDescriptionMI; ++ break; ++ default: ++ preInterruptActionMessageArgs = reservedDescription; ++ break; ++ } ++ ++ if (preInterruptActionNone != convertForMessage(preTimeoutInterruptAction)) ++ { ++ preTimeoutInterruptOccurFlag(true); ++ ++ sd_journal_send("MESSAGE=IPMIWatchdog: Pre Timed out Interrupt=%s", ++ convertForMessage(preTimeoutInterruptAction).c_str(), ++ "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s", ++ "OpenBMC.0.1.IPMIWatchdog", ++ "REDFISH_MESSAGE_ARGS=Timer interrupt - %s due to " ++ "Watchdog timeout. timer use: %s", ++ preInterruptActionMessageArgs.c_str(), ++ timeUserMessage.c_str(), NULL); ++ ++ if (preTimeoutInterruptAction == ++ Watchdog::PreTimeoutInterruptAction::NMI) ++ { ++ sdbusplus::message::message preTimeoutInterruptHandler; ++ preTimeoutInterruptHandler = bus.new_method_call( ++ nmi::busName, nmi::path, nmi::interface, nmi::request); ++ bus.call_noreply(preTimeoutInterruptHandler); ++ } ++ } + + auto target = actionTargetMap.find(action); + if (target == actionTargetMap.end()) +@@ -147,12 +323,45 @@ void Watchdog::timeOutHandler() + + try + { +- auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT, +- SYSTEMD_INTERFACE, "StartUnit"); +- method.append(target->second); +- method.append("replace"); ++ sdbusplus::message::message method; ++ if (action == Watchdog::Action::HardReset) ++ { ++ auto method = bus.new_method_call( ++ restart::busName, restart::path, ++ "org.freedesktop.DBus.Properties", "Set"); ++ method.append( ++ restart::interface, restart::property, ++ std::variant<std::string>("xyz.openbmc_project.State.Host." ++ "RestartCause.WatchdogTimer")); ++ bus.call_noreply(method); + +- bus.call_noreply(method); ++ method = bus.new_method_call(host::busName, host::path, ++ "org.freedesktop.DBus.Properties", ++ "Set"); ++ method.append(host::interface, host::request, ++ std::variant<std::string>(target->second)); ++ bus.call_noreply(method); ++ } ++ else ++ { ++ if (action == Watchdog::Action::PowerCycle) ++ { ++ auto method = bus.new_method_call( ++ restart::busName, restart::path, ++ "org.freedesktop.DBus.Properties", "Set"); ++ method.append(restart::interface, restart::property, ++ std::variant<std::string>( ++ "xyz.openbmc_project.State.Host." ++ "RestartCause.WatchdogTimer")); ++ bus.call_noreply(method); ++ } ++ method = bus.new_method_call(chassis::busName, chassis::path, ++ "org.freedesktop.DBus.Properties", ++ "Set"); ++ method.append(chassis::interface, chassis::request, ++ std::variant<std::string>(target->second)); ++ bus.call_noreply(method); ++ } + } + catch (const SdBusError& e) + { +diff --git a/src/watchdog.hpp b/src/watchdog.hpp +index 736e71f68fcc..79158f192f40 100644 +--- a/src/watchdog.hpp ++++ b/src/watchdog.hpp +@@ -73,7 +73,17 @@ class Watchdog : public WatchdogInherits + bus(bus), actionTargetMap(std::move(actionTargetMap)), + fallback(std::move(fallback)), minInterval(minInterval), + timer(event, std::bind(&Watchdog::timeOutHandler, this)), +- objPath(objPath) ++ objPath(objPath), powerStateChangedSignal( ++ bus, ++ sdbusplus::bus::match::rules::propertiesChanged( ++ "/xyz/openbmc_project/state/host0", ++ "xyz.openbmc_project.State.Host"), ++ [this](sdbusplus::message::message& msg) { ++ std::string objectName; ++ std::map<std::string, std::variant<std::string>> props; ++ msg.read(objectName, props); ++ powerStateChangedHandler(props); ++ }) + { + // Use default if passed in otherwise just use default that comes + // with object +@@ -90,6 +100,12 @@ class Watchdog : public WatchdogInherits + tryFallbackOrDisable(); + } + ++ /** @brief Disable watchdog when power status change meet ++ * the specific requirement ++ */ ++ void powerStateChangedHandler( ++ const std::map<std::string, std::variant<std::string>>& props); ++ + /** @brief Resets the TimeRemaining to the configured Interval + * Optionally enables the watchdog. + * +@@ -178,6 +194,10 @@ class Watchdog : public WatchdogInherits + /** @brief Contained timer object */ + sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer; + ++ /** @brief Optional Callback handler when power status change meet ++ * the specific requirement */ ++ sdbusplus::bus::match_t powerStateChangedSignal; ++ + /** @brief Optional Callback handler on timer expirartion */ + void timeOutHandler(); + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service new file mode 100644 index 000000000..35d3e8e0f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service @@ -0,0 +1,15 @@ +[Unit] +Description=Phosphor Watchdog + +[Service] +ExecStart=/usr/bin/phosphor-watchdog --continue --service=xyz.openbmc_project.Watchdog \ + --path=/xyz/openbmc_project/watchdog/host0 \ + --action_target=xyz.openbmc_project.State.Watchdog.Action.HardReset=xyz.openbmc_project.State.Host.Transition.ForceWarmReboot \ + --action_target=xyz.openbmc_project.State.Watchdog.Action.PowerOff=xyz.openbmc_project.State.Chassis.Transition.Off \ + --action_target=xyz.openbmc_project.State.Watchdog.Action.PowerCycle=xyz.openbmc_project.State.Chassis.Transition.PowerCycle + +BusName=xyz.openbmc_project.Watchdog +Type=dbus + +[Install] +WantedBy=basic.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend new file mode 100644 index 000000000..cc439b2c5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend @@ -0,0 +1,8 @@ +FILESEXTRAPATHS:append := ":${THISDIR}/${PN}" + +SRC_URI += "file://0001-Customize-phosphor-watchdog-for-Intel-platforms.patch \ + " + +# Remove the override to keep service running after DC cycle +SYSTEMD_OVERRIDE:${PN}:remove = "poweron.conf:phosphor-watchdog@poweron.service.d/poweron.conf" +SYSTEMD_SERVICE:${PN} = "phosphor-watchdog.service" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb new file mode 100644 index 000000000..ac1302d9c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb @@ -0,0 +1,12 @@ +SUMMARY = "System watchdog" +DESCRIPTION = "BMC hardware watchdog service that is used to reset BMC \ + when unrecoverable events occurs" + +inherit allarch +inherit obmc-phosphor-systemd + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SYSTEMD_SERVICE:${PN} += "system-watchdog.service" +SYSTEMD_ENVIRONMENT_FILE:${PN} += "obmc/system-watchdog/system-watchdog.conf" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf new file mode 100644 index 000000000..defe830a1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf @@ -0,0 +1,3 @@ +TIMEOUT=60 +INTERVAL=10 +DEVICE=/dev/watchdog1 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service new file mode 100644 index 000000000..1564fda20 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service @@ -0,0 +1,11 @@ +[Unit] +Description=BMC Hardware Watchdog Daemon + +[Service] +EnvironmentFile=/etc/default/obmc/system-watchdog/system-watchdog.conf +ExecStart=/sbin/watchdog -T ${{TIMEOUT}} -t ${{INTERVAL}} -F ${{DEVICE}} +KillSignal=SIGKILL + +[Install] +WantedBy=basic.target + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/config.json b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/config.json new file mode 100644 index 000000000..348a7792d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/config.json @@ -0,0 +1,9 @@ +{ + "customKeyEnable": true, + "keyType" : "VT100+", + "customConsoleDisplaySize": { + "width": 100, + "height": 32 + }, + "VirtualMediaEnabled" : true +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui_%.bbappend new file mode 100644 index 000000000..19db93805 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui_%.bbappend @@ -0,0 +1,4 @@ +SRC_URI = "git://github.com/Intel-BMC/phosphor-webui;protocol=ssh;branch=intel2" +FILESEXTRAPATHS:prepend:intel := "${THISDIR}/${PN}:" + +SRCREV = "b5707d7648de19350b3f308b9602c888c6418d6f" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue/login-company-logo.svg b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue/login-company-logo.svg new file mode 100644 index 000000000..0976f72e8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue/login-company-logo.svg @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 395.4 155.9" style="enable-background:new 0 0 395.4 155.9;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#0068B5;}
+</style>
+<rect x="4.7" y="5.2" class="st0" width="28.1" height="28.1"/>
+<g>
+ <path d="M32.1,151.6V50.4H5.5v101.2H32.1z M208.9,152.6v-24.8c-3.9,0-7.2-0.2-9.6-0.6c-2.8-0.4-4.9-1.4-6.3-2.8
+ c-1.4-1.4-2.3-3.4-2.8-6c-0.4-2.5-0.6-5.8-0.6-9.8V73.2h19.3V50.4h-19.3V10.9h-26.7v97.9c0,8.3,0.7,15.3,2.1,20.9
+ c1.4,5.5,3.8,10,7.1,13.4s7.7,5.8,13,7.3c5.4,1.5,12.2,2.2,20.3,2.2L208.9,152.6L208.9,152.6z M361.7,151.6V3.1H335v148.5H361.7z
+ M137.2,60.3c-7.4-8-17.8-12-31-12c-6.4,0-12.2,1.3-17.5,3.9C83.5,54.8,79,58.4,75.5,63L74,64.9v-1.7V50.4H47.7v101.2h26.5V97.7
+ v3.7c0-0.6,0-1.2,0-1.8c0.3-9.5,2.6-16.5,7-21c4.7-4.8,10.4-7.2,16.9-7.2c7.7,0,13.6,2.4,17.5,7c3.8,4.6,5.8,11.1,5.8,19.4l0,0V98
+ l0,0l0,0v53.5h26.9V94.1C148.4,79.7,144.6,68.3,137.2,60.3z M321.2,100.8c0-7.3-1.3-14.1-3.8-20.5c-2.6-6.3-6.2-11.9-10.7-16.7
+ c-4.6-4.8-10.1-8.5-16.5-11.2s-13.5-4-21.2-4c-7.3,0-14.2,1.4-20.6,4.1c-6.4,2.8-12,6.5-16.7,11.2s-8.5,10.3-11.2,16.7
+ c-2.8,6.4-4.1,13.3-4.1,20.6c0,7.3,1.3,14.2,3.9,20.6c2.6,6.4,6.3,12,10.9,16.7c4.6,4.7,10.3,8.5,16.9,11.2
+ c6.6,2.8,13.9,4.2,21.7,4.2c22.6,0,36.6-10.3,45-19.9l-19.2-14.6c-4,4.8-13.6,11.3-25.6,11.3c-7.5,0-13.7-1.7-18.4-5.2
+ c-4.7-3.4-7.9-8.2-9.6-14.1l-0.3-0.9h79.5L321.2,100.8L321.2,100.8z M241.9,91.5c0-7.4,8.5-20.3,26.8-20.4
+ c18.3,0,26.9,12.9,26.9,20.3L241.9,91.5z"/>
+ <path d="M392.1,138.4c-0.5-1.2-1.2-2.2-2.1-3.1c-0.9-0.9-1.9-1.6-3.1-2.1s-2.5-0.8-3.8-0.8c-1.4,0-2.6,0.3-3.8,0.8
+ c-1.2,0.5-2.2,1.2-3.1,2.1c-0.9,0.9-1.6,1.9-2.1,3.1c-0.5,1.2-0.8,2.5-0.8,3.8c0,1.4,0.3,2.6,0.8,3.8s1.2,2.2,2.1,3.1
+ c0.9,0.9,1.9,1.6,3.1,2.1s2.5,0.8,3.8,0.8c1.4,0,2.6-0.3,3.8-0.8c1.2-0.5,2.2-1.2,3.1-2.1c0.9-0.9,1.6-1.9,2.1-3.1
+ c0.5-1.2,0.8-2.5,0.8-3.8S392.6,139.6,392.1,138.4z M390.5,145.4c-0.4,1-1,1.9-1.7,2.6c-0.7,0.7-1.6,1.3-2.6,1.7s-2,0.6-3.2,0.6
+ c-1.1,0-2.2-0.2-3.2-0.6c-1-0.4-1.9-1-2.6-1.7s-1.3-1.6-1.7-2.6c-0.4-1-0.6-2-0.6-3.2c0-1.1,0.2-2.2,0.6-3.2s1-1.9,1.7-2.6
+ c0.7-0.7,1.6-1.3,2.6-1.7s2-0.6,3.2-0.6c1.1,0,2.2,0.2,3.2,0.6c1,0.4,1.9,1,2.6,1.7s1.3,1.6,1.7,2.6c0.4,1,0.6,2,0.6,3.2
+ C391.2,143.4,390.9,144.4,390.5,145.4z M384.9,143c0.8-0.1,1.4-0.4,1.9-0.9s0.8-1.2,0.8-2.2c0-1.1-0.3-1.9-1-2.5
+ c-0.6-0.6-1.7-0.9-3-0.9h-4.4v11.3h2.1v-4.6h1.5l2.8,4.6h2.2L384.9,143z M383.8,141.4c-0.3,0-0.6,0-1,0h-1.5v-3.2h1.5
+ c0.3,0,0.6,0,1,0c0.3,0,0.6,0.1,0.9,0.2c0.3,0.1,0.5,0.3,0.6,0.5s0.2,0.5,0.2,0.9s-0.1,0.7-0.2,0.9c-0.2,0.2-0.4,0.4-0.6,0.5
+ C384.4,141.3,384.1,141.4,383.8,141.4z"/>
+</g>
+</svg>
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue/logo-header.svg b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue/logo-header.svg new file mode 100644 index 000000000..dc449a1b6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue/logo-header.svg @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="81px" height="32px"
+ viewBox="0 0 395.4 155.9" style="enable-background:new 0 0 395.4 155.9;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#00C7FD;}
+ .st1{fill:#FFFFFF;}
+</style>
+<rect x="4.7" y="5.2" class="st0" width="28.1" height="28.1"/>
+<path class="st1" d="M32.1,151.6V50.3H5.4v101.2H32.1z M208.9,152.6v-24.8c-3.9,0-7.2-0.2-9.6-0.6c-2.8-0.4-4.9-1.4-6.3-2.8
+ c-1.4-1.4-2.3-3.4-2.8-6.1c-0.4-2.5-0.6-5.8-0.6-9.8V73.2h19.3V50.3h-19.3V10.9h-26.7v97.9c0,8.3,0.7,15.3,2.1,20.9
+ c1.4,5.5,3.8,10,7.1,13.4c3.3,3.4,7.7,5.8,13,7.3c5.4,1.5,12.2,2.2,20.3,2.2H208.9z M361.7,151.6V3.1h-26.7v148.5H361.7z
+ M137.2,60.3c-7.4-8-17.8-12-31-12c-6.4,0-12.3,1.3-17.5,3.9C83.4,54.8,79,58.4,75.4,63L74,64.9l0-1.7V50.3H47.7v101.2h26.5V97.6
+ l0,3.7c0-0.6,0-1.2,0-1.8c0.3-9.5,2.6-16.5,7-21c4.7-4.8,10.4-7.2,16.9-7.2c7.7,0,13.6,2.4,17.5,7c3.8,4.6,5.8,11.1,5.8,19.4l0,0V98
+ c0,0,0,0,0,0l0,0l0,53.5h26.9V94.1C148.4,79.7,144.6,68.3,137.2,60.3z M321.2,100.8c0-7.3-1.3-14.1-3.8-20.5
+ c-2.6-6.3-6.2-11.9-10.7-16.7c-4.6-4.8-10.1-8.5-16.5-11.2c-6.4-2.7-13.5-4-21.2-4c-7.3,0-14.2,1.4-20.6,4.1
+ c-6.4,2.8-12,6.5-16.7,11.2c-4.7,4.7-8.5,10.3-11.2,16.7c-2.8,6.4-4.1,13.3-4.1,20.6c0,7.3,1.3,14.2,3.9,20.6
+ c2.6,6.4,6.3,12,10.9,16.7c4.6,4.7,10.3,8.5,16.9,11.2c6.6,2.8,13.9,4.2,21.7,4.2c22.6,0,36.6-10.3,45-19.9l-19.2-14.6
+ c-4,4.8-13.6,11.3-25.6,11.3c-7.5,0-13.7-1.7-18.4-5.2c-4.7-3.4-7.9-8.2-9.6-14.1l-0.3-0.9h79.5V100.8z M241.9,91.5
+ c0-7.4,8.5-20.3,26.8-20.4c18.3,0,26.9,12.9,26.9,20.3L241.9,91.5z M392.1,138.8c-0.5-1.2-1.2-2.2-2.1-3.1c-0.9-0.9-1.9-1.6-3.1-2.1
+ c-1.2-0.5-2.5-0.8-3.8-0.8c-1.4,0-2.6,0.3-3.8,0.8c-1.2,0.5-2.2,1.2-3.1,2.1c-0.9,0.9-1.6,1.9-2.1,3.1c-0.5,1.2-0.8,2.5-0.8,3.8
+ c0,1.4,0.3,2.6,0.8,3.8s1.2,2.2,2.1,3.1c0.9,0.9,1.9,1.6,3.1,2.1c1.2,0.5,2.5,0.8,3.8,0.8c1.4,0,2.6-0.3,3.8-0.8
+ c1.2-0.5,2.2-1.2,3.1-2.1c0.9-0.9,1.6-1.9,2.1-3.1c0.5-1.2,0.8-2.5,0.8-3.8C392.9,141.3,392.6,140,392.1,138.8z M390.5,145.8
+ c-0.4,1-1,1.9-1.7,2.6c-0.7,0.7-1.6,1.3-2.6,1.7c-1,0.4-2,0.6-3.2,0.6c-1.1,0-2.2-0.2-3.2-0.6c-1-0.4-1.9-1-2.6-1.7
+ c-0.7-0.7-1.3-1.6-1.7-2.6c-0.4-1-0.6-2-0.6-3.2c0-1.1,0.2-2.2,0.6-3.2c0.4-1,1-1.9,1.7-2.6c0.7-0.7,1.6-1.3,2.6-1.7
+ c1-0.4,2-0.6,3.2-0.6c1.1,0,2.2,0.2,3.2,0.6c1,0.4,1.9,1,2.6,1.7c0.7,0.7,1.3,1.6,1.7,2.6c0.4,1,0.6,2,0.6,3.2
+ C391.1,143.8,390.9,144.8,390.5,145.8z M384.9,143.5c0.8-0.1,1.4-0.4,1.9-0.9c0.5-0.5,0.8-1.2,0.8-2.2c0-1.1-0.3-1.9-1-2.5
+ c-0.6-0.6-1.7-0.9-3-0.9h-4.4v11.3h2.1v-4.6h1.5l2.8,4.6h2.2L384.9,143.5z M383.8,141.8c-0.3,0-0.6,0-1,0h-1.5v-3.2h1.5
+ c0.3,0,0.6,0,1,0c0.3,0,0.6,0.1,0.9,0.2c0.3,0.1,0.5,0.3,0.6,0.5c0.2,0.2,0.2,0.5,0.2,0.9c0,0.4-0.1,0.7-0.2,0.9
+ c-0.2,0.2-0.4,0.4-0.6,0.5C384.4,141.7,384.1,141.8,383.8,141.8z"/>
+</svg>
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue_%.bbappend new file mode 100644 index 000000000..1273ebb29 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue_%.bbappend @@ -0,0 +1,15 @@ +# Enable downstream autobump +SRC_URI = "git://github.com/openbmc/webui-vue.git" +SRCREV = "2a2e1021f48e2a939859ba7f4ae86c5de6df5655" + +FILESEXTRAPATHS:append := "${THISDIR}/${PN}:" +SRC_URI += " \ + file://login-company-logo.svg \ + file://logo-header.svg \ + " + +do_compile:prepend() { + cp -vf ${S}/.env.intel ${S}/.env + cp -vf ${WORKDIR}/login-company-logo.svg ${S}/src/assets/images + cp -vf ${WORKDIR}/logo-header.svg ${S}/src/assets/images +} |