diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor')
160 files changed, 18183 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..1bf81d953 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/beepcode-mgr/beepcode-mgr.bb @@ -0,0 +1,25 @@ + +SUMMARY = "Beep code manager service" +DESCRIPTION = "The beep code manager service will provide a method for beep code" + +SRC_URI = "\ + file://CMakeLists.txt \ + file://beepcode_mgr.cpp \ + " +PV = "0.1" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +S = "${WORKDIR}" + +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/configuration/entity-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend new file mode 100644 index 000000000..2f1f1dc6a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend @@ -0,0 +1,6 @@ +# this is here just to bump faster than upstream +# SRC_URI = "git://github.com/openbmc/entity-manager.git" +SRCREV = "c7c5de795cf672797ae35965e95642dd1fc39363" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + 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..7fb8f79d3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/console/obmc-console/obmc-console@.service @@ -0,0 +1,21 @@ +[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 +ExecStartPre=/bin/sh -c 'echo -n "uart3" > /sys/bus/platform/drivers/aspeed-uart-routing/1e78909c.uart_routing/uart1' +ExecStartPre=/bin/sh -c 'echo -n "uart1" > /sys/bus/platform/drivers/aspeed-uart-routing/1e78909c.uart_routing/uart3' +ExecStartPre=/bin/sh -c 'echo -n "io1" > /sys/bus/platform/drivers/aspeed-uart-routing/1e78909c.uart_routing/uart4' +ExecStartPre=/bin/sh -c 'echo -n "uart4" > /sys/bus/platform/drivers/aspeed-uart-routing/1e78909c.uart_routing/io1' +ExecStart=/usr/bin/env obmc-console-server --config {sysconfdir}/obmc-console.conf %i +ExecStopPost=/bin/sh -c 'echo -n "io1" > /sys/bus/platform/drivers/aspeed-uart-routing/1e78909c.uart_routing/uart1' +ExecStopPost=/bin/sh -c 'echo -n "io3" > /sys/bus/platform/drivers/aspeed-uart-routing/1e78909c.uart_routing/uart3' +ExecStopPost=/bin/sh -c 'echo -n "io4" > /sys/bus/platform/drivers/aspeed-uart-routing/1e78909c.uart_routing/uart4' +ExecStopPost=/bin/sh -c 'echo -n "uart1" > /sys/bus/platform/drivers/aspeed-uart-routing/1e78909c.uart_routing/io1' +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..09510fec8 --- /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..089aaf59f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync.bb @@ -0,0 +1,26 @@ + +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 \ + file://pch-time-sync.cpp \ + " +PV = "0.1" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +S = "${WORKDIR}" + +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..00e2b53fe --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.cpp @@ -0,0 +1,417 @@ +/* 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 <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; + } + phosphor::logging::log<phosphor::logging::level::INFO>( + "Update BMC time to: ", + phosphor::logging::entry("TIME=%s", dateString.c_str())); + } + + // 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..18780dfdd --- /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..c87b2d89d --- /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 49debd0955b672d591f35e74119b288bd6df2992 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] [ipmi] set BIOS id + +change#2 +add new dbus interface for BIOS attributes + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + xyz/openbmc_project/Inventory/Item/Bios.interface.yaml | 9 +++++++++ + 1 file changed, 9 insertions(+) + create mode 100644 xyz/openbmc_project/Inventory/Item/Bios.interface.yaml + +diff --git a/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml b/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml +new file mode 100644 +index 0000000..d7a6b95 +--- /dev/null ++++ b/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.16.2 + 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..2c9344306 --- /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 631deef0ca88a77283741edeae8078d2185f414c 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> +--- + xyz/openbmc_project/State/Watchdog.interface.yaml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xyz/openbmc_project/State/Watchdog.interface.yaml b/xyz/openbmc_project/State/Watchdog.interface.yaml +index f76dbf2..402e1a8 100644 +--- a/xyz/openbmc_project/State/Watchdog.interface.yaml ++++ b/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.7.4 + 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..9052435ca --- /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 eeac4cf4528994aeb213d549daf4c033ac9d3bbc 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 xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml + +diff --git a/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml b/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml +new file mode 100644 +index 0000000..55ee80a +--- /dev/null ++++ b/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.0 + 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..9471c7ab2 --- /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 7260c24b201759f3a5168eebfee215072c13e641 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 xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml + +diff --git a/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml b/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml +new file mode 100644 +index 0000000..2304263 +--- /dev/null ++++ b/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.0 + 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..67fa59090 --- /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 6e9a19c43acac7d4254910906329d98d7b59085a 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> +--- + xyz/openbmc_project/State/Watchdog.interface.yaml | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/xyz/openbmc_project/State/Watchdog.interface.yaml b/xyz/openbmc_project/State/Watchdog.interface.yaml +index 2fc47d8..6dfa9b9 100644 +--- a/xyz/openbmc_project/State/Watchdog.interface.yaml ++++ b/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.7.4 + 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..d7e66abd2 --- /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 b7c487750c05dcc081219ccdd4ef539beef6aa30 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> +--- + xyz/openbmc_project/State/Watchdog.interface.yaml | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/xyz/openbmc_project/State/Watchdog.interface.yaml b/xyz/openbmc_project/State/Watchdog.interface.yaml +index bf4cca0..6579368 100644 +--- a/xyz/openbmc_project/State/Watchdog.interface.yaml ++++ b/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.7.4 + 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..36c63ec58 --- /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 0d6556539be9bda478a3cabb6127eace5764fa11 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> +--- + xyz/openbmc_project/Software/Activation.interface.yaml | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/xyz/openbmc_project/Software/Activation.interface.yaml b/xyz/openbmc_project/Software/Activation.interface.yaml +index efeeeb6..6c9ce75 100644 +--- a/xyz/openbmc_project/Software/Activation.interface.yaml ++++ b/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. +@@ -48,6 +56,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.7.4 + 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..e6afc0117 --- /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,457 @@ +From 65d1772312a76ebfdac6391bb97287c62f18c43f 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 +--- + xyz/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 | 13 + + xyz/openbmc_project/MCTP/README.md | 43 ++++ + .../MCTP/SupportedMessageTypes.interface.yaml | 36 +++ + 7 files changed, 382 insertions(+) + create mode 100644 xyz/openbmc_project/MCTP/Base.interface.yaml + create mode 100644 xyz/openbmc_project/MCTP/Binding/PCIe.interface.yaml + create mode 100644 xyz/openbmc_project/MCTP/Binding/SMBus.interface.yaml + create mode 100644 xyz/openbmc_project/MCTP/BusOwner.interface.yaml + create mode 100644 xyz/openbmc_project/MCTP/Endpoint.interface.yaml + create mode 100644 xyz/openbmc_project/MCTP/README.md + create mode 100644 xyz/openbmc_project/MCTP/SupportedMessageTypes.interface.yaml + +diff --git a/xyz/openbmc_project/MCTP/Base.interface.yaml b/xyz/openbmc_project/MCTP/Base.interface.yaml +new file mode 100644 +index 0000000..9438551 +--- /dev/null ++++ b/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/xyz/openbmc_project/MCTP/Binding/PCIe.interface.yaml b/xyz/openbmc_project/MCTP/Binding/PCIe.interface.yaml +new file mode 100644 +index 0000000..1bd2881 +--- /dev/null ++++ b/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/xyz/openbmc_project/MCTP/Binding/SMBus.interface.yaml b/xyz/openbmc_project/MCTP/Binding/SMBus.interface.yaml +new file mode 100644 +index 0000000..9219ad0 +--- /dev/null ++++ b/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/xyz/openbmc_project/MCTP/BusOwner.interface.yaml b/xyz/openbmc_project/MCTP/BusOwner.interface.yaml +new file mode 100644 +index 0000000..d46298e +--- /dev/null ++++ b/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/xyz/openbmc_project/MCTP/Endpoint.interface.yaml b/xyz/openbmc_project/MCTP/Endpoint.interface.yaml +new file mode 100644 +index 0000000..e4ba4d0 +--- /dev/null ++++ b/xyz/openbmc_project/MCTP/Endpoint.interface.yaml +@@ -0,0 +1,13 @@ ++description: ++ Interface exposed by discovered MCTP endpoints. ++ ++properties: ++ - name: Mode ++ type: enum[xyz.openbmc_project.MCTP.Base.BindingModeTypes] ++ description: Endpoint / BusOwner / Bridge ++ ++ - name: NetworkId ++ type: uint16 ++ description: > ++ MCTP network ID a unique identifier to distinguish each independent ++ MCTP network within a platform. +diff --git a/xyz/openbmc_project/MCTP/README.md b/xyz/openbmc_project/MCTP/README.md +new file mode 100644 +index 0000000..c819dbb +--- /dev/null ++++ b/xyz/openbmc_project/MCTP/README.md +@@ -0,0 +1,43 @@ ++# MCTP Daemon ++ ++## Overview ++MCTP service exposes D-Bus methods / properties / signals for managing ++MCTP devices or work as MCTP Endpoint. MCTP daemon will either ++work in Bus Owner or Endpoint mode for the specified physical medium. ++ ++### MCTP service ++MCTP service can be started either in Bus Owner mode or Endpoint mode. ++It will expose following objects. ++1. Base object ++2. MCTP Endpoints (discovered in case of Bus Owner mode, queried using ++routing table in case of Endpoint mode) ++Please refer individual yaml file for details about the ++methods / signals / properties exposed in the interfaces. ++ ++#### Base object ++Exposed under the path `/xyz/openbmc_project/mctp` with the following ++interfaces. ++1. `xyz.openbmc_project.MCTP.Base` which exposes all the common properties ++needed for MCTP Daemon. ++2. `xyz.openbmc_project.MCTP.BusOwner` available only in Bus Owner mode ++which exposes the properties needed by Bus Owner MCTP Daemon. ++3. `xyz.openbmc_project.MCTP.SupportedMessageTypes` which exposes the message ++types supported. ++4. Binding interface `xyz.openbmc_project.MCTP.Binding.PCIe` or ++`xyz.openbmc_project.MCTP.Binding.SMBus` as per the physical medium in which ++this MCTP Daemon is instantiated. ++5. Common UUID interface `xyz.openbmc_project.Common.UUID` which exposes UUID ++in RFC4122 format. ++ ++#### Endpoint object ++Exposed under the path `/xyz/openbmc_project/mctp/device/<eid>` with the ++following interfaces. ++1. `xyz.openbmc_project.MCTP.SupportedMessageTypes` which exposes supported MCTP ++message types for the discovered MCTP Endpoint. ++2. `xyz.openbmc_project.MCTP.Endpoint` which exposes properties like Network ID ++and endpoint mode (to identify Bus Owner or Bridge or Endpoint) for the discovered ++MCTP Endpoint. ++3. `xyz.openbmc_project.MCTP.Bridge` available only for discovered MCTP Bridges to ++expose properties like EID pool. (TBD) ++4. Common UUID interface `xyz.openbmc_project.Common.UUID` which exposes UUID ++in RFC4122 format. +diff --git a/xyz/openbmc_project/MCTP/SupportedMessageTypes.interface.yaml b/xyz/openbmc_project/MCTP/SupportedMessageTypes.interface.yaml +new file mode 100644 +index 0000000..fa447ee +--- /dev/null ++++ b/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..5ee550eaa --- /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 e7ba5f63a551099e1b5f934683e163963f28f28e Mon Sep 17 00:00:00 2001 +From: "Gade-RajasekharReddy@" <raja.sekhar.reddy.gade@linux.intel.com> +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 +++ + xyz/openbmc_project/PLDM/FWU/README.md | 103 ++++++++++++++++++ + 12 files changed, 378 insertions(+) + create mode 100644 xyz/openbmc_project/PLDM/FWU/ACPIDescriptor.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/FWUBase.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/IANADescriptor.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/PCIDescriptor.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/PendingComponentInfo.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/PnPDescriptor.interface.yaml + create mode 100644 xyz/openbmc_project/PLDM/FWU/README.md + +diff --git a/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor.interface.yaml b/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor.interface.yaml +new file mode 100644 +index 0000000..e225bad +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo.interface.yaml b/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo.interface.yaml +new file mode 100644 +index 0000000..94115a3 +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo.interface.yaml b/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo.interface.yaml +new file mode 100644 +index 0000000..77a7566 +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate.interface.yaml b/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate.interface.yaml +new file mode 100644 +index 0000000..36560ff +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods.interface.yaml b/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods.interface.yaml +new file mode 100644 +index 0000000..d5ec47c +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/FWUBase.interface.yaml b/xyz/openbmc_project/PLDM/FWU/FWUBase.interface.yaml +new file mode 100644 +index 0000000..2ba15e2 +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/IANADescriptor.interface.yaml b/xyz/openbmc_project/PLDM/FWU/IANADescriptor.interface.yaml +new file mode 100644 +index 0000000..c013955 +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/PCIDescriptor.interface.yaml b/xyz/openbmc_project/PLDM/FWU/PCIDescriptor.interface.yaml +new file mode 100644 +index 0000000..8d758ed +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo.interface.yaml b/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo.interface.yaml +new file mode 100644 +index 0000000..3861572 +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo.interface.yaml b/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo.interface.yaml +new file mode 100644 +index 0000000..59a2ad8 +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/PnPDescriptor.interface.yaml b/xyz/openbmc_project/PLDM/FWU/PnPDescriptor.interface.yaml +new file mode 100644 +index 0000000..801db6d +--- /dev/null ++++ b/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/xyz/openbmc_project/PLDM/FWU/README.md b/xyz/openbmc_project/PLDM/FWU/README.md +new file mode 100644 +index 0000000..2931739 +--- /dev/null ++++ b/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..3a1ae57fb --- /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 007c07561e03a005e90858f77266f4fba3e8e2c9 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> +--- + xyz/openbmc_project/Software/Version.interface.yaml | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/xyz/openbmc_project/Software/Version.interface.yaml b/xyz/openbmc_project/Software/Version.interface.yaml +index 345e5b5..f2efbec 100644 +--- a/xyz/openbmc_project/Software/Version.interface.yaml ++++ b/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..fd31665dc --- /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,156 @@ +From cd3c2ff290e6ff205c32b386c7c8a73d4a8980e5 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 1/4] update meson build files for control and bios + +Signed-off-by: Zhikui Ren <zhikui.ren@intel.com> +--- + .../Control/Power/RestoreDelay/meson.build | 14 ++++++++++++++ + gen/xyz/openbmc_project/Control/Power/meson.build | 15 +++++++++++++++ + .../Control/Processor/ErrConfig/meson.build | 14 ++++++++++++++ + .../openbmc_project/Control/Processor/meson.build | 15 +++++++++++++++ + .../Inventory/Item/Bios/meson.build | 14 ++++++++++++++ + .../openbmc_project/Inventory/Item/meson.build | 15 +++++++++++++++ + 6 files changed, 87 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 0000000..91581fd +--- /dev/null ++++ b/gen/xyz/openbmc_project/Control/Power/RestoreDelay/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/Control/Power/RestoreDelay__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 05628ec..ad04479 100644 +--- a/gen/xyz/openbmc_project/Control/Power/meson.build ++++ b/gen/xyz/openbmc_project/Control/Power/meson.build +@@ -29,6 +29,21 @@ generated_others += custom_target( + build_by_default: true, + ) + ++subdir('RestoreDelay') ++generated_others += custom_target( ++ 'xyz/openbmc_project/Control/Power/RestoreDelay__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/Control/Power/RestoreDelay', ++ ], ++ build_by_default: true, ++) ++ + 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 0000000..12961c2 +--- /dev/null ++++ b/gen/xyz/openbmc_project/Control/Processor/ErrConfig/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/Control/Processor/ErrConfig__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 adbff0a..816c30d 100644 +--- a/gen/xyz/openbmc_project/Control/Processor/meson.build ++++ b/gen/xyz/openbmc_project/Control/Processor/meson.build +@@ -14,3 +14,18 @@ generated_others += custom_target( + build_by_default: true, + ) + ++subdir('ErrConfig') ++generated_others += custom_target( ++ 'xyz/openbmc_project/Control/Processor/ErrConfig__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/Control/Processor/ErrConfig', ++ ], ++ build_by_default: true, ++) ++ +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 0000000..5c6fce0 +--- /dev/null ++++ b/gen/xyz/openbmc_project/Inventory/Item/Bios/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/Inventory/Item/Bios__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 145bba2..3e036bd 100644 +--- a/gen/xyz/openbmc_project/Inventory/Item/meson.build ++++ b/gen/xyz/openbmc_project/Inventory/Item/meson.build +@@ -27,6 +27,21 @@ generated_others += custom_target( + build_by_default: true, + ) + ++subdir('Bios') ++generated_others += custom_target( ++ 'xyz/openbmc_project/Inventory/Item/Bios__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/Inventory/Item/Bios', ++ ], ++ build_by_default: true, ++) ++ + 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..5f41a1348 --- /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,266 @@ +From b25ae31fa674a287bc100081a9dfc243bcf53f19 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 | 14 +++++ + .../MCTP/Binding/PCIe/meson.build | 14 +++++ + .../MCTP/Binding/SMBus/meson.build | 14 +++++ + .../openbmc_project/MCTP/Binding/meson.build | 31 ++++++++++ + .../openbmc_project/MCTP/BusOwner/meson.build | 14 +++++ + .../openbmc_project/MCTP/Endpoint/meson.build | 14 +++++ + .../MCTP/SupportedMessageTypes/meson.build | 14 +++++ + gen/xyz/openbmc_project/MCTP/meson.build | 62 +++++++++++++++++++ + gen/xyz/openbmc_project/meson.build | 1 + + 9 files changed, 178 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/Endpoint/meson.build + create mode 100644 gen/xyz/openbmc_project/MCTP/SupportedMessageTypes/meson.build + create mode 100644 gen/xyz/openbmc_project/MCTP/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 0000000..81aeb86 +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/Base/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/Base__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..0da866c +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/Binding/PCIe/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/Binding/PCIe__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..a0f97bd +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/Binding/SMBus/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/Binding/SMBus__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..6e3407c +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/Binding/meson.build +@@ -0,0 +1,31 @@ ++# Generated file; do not modify. ++subdir('PCIe') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/Binding/PCIe__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/MCTP/Binding/PCIe', ++ ], ++ build_by_default: true, ++) ++ ++subdir('SMBus') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/Binding/SMBus__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/MCTP/Binding/SMBus', ++ ], ++ build_by_default: true, ++) ++ +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 0000000..190a640 +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/BusOwner/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/BusOwner__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/MCTP/BusOwner', ++ ], ++) ++ +diff --git a/gen/xyz/openbmc_project/MCTP/Endpoint/meson.build b/gen/xyz/openbmc_project/MCTP/Endpoint/meson.build +new file mode 100644 +index 0000000..cababfb +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/Endpoint/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/Endpoint__cpp'.underscorify(), ++ input: [ meson.source_root() / 'xyz/openbmc_project/MCTP/Endpoint.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.source_root(), ++ 'xyz/openbmc_project/MCTP/Endpoint', ++ ], ++) ++ +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 0000000..f58fa44 +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/SupportedMessageTypes/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/MCTP/SupportedMessageTypes__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/MCTP/SupportedMessageTypes', ++ ], ++) ++ +diff --git a/gen/xyz/openbmc_project/MCTP/meson.build b/gen/xyz/openbmc_project/MCTP/meson.build +new file mode 100644 +index 0000000..94ab2c2 +--- /dev/null ++++ b/gen/xyz/openbmc_project/MCTP/meson.build +@@ -0,0 +1,62 @@ ++# Generated file; do not modify. ++subdir('Base') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/Base__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/MCTP/Base', ++ ], ++ build_by_default: true, ++) ++ ++subdir('Binding') ++subdir('BusOwner') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/BusOwner__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/MCTP/BusOwner', ++ ], ++ build_by_default: true, ++) ++ ++subdir('Endpoint') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/Endpoint__markdown'.underscorify(), ++ input: [ meson.source_root() / 'xyz/openbmc_project/MCTP/Endpoint.interface.yaml', ], ++ output: [ 'Endpoint.md' ], ++ command: [ ++ sdbuspp_gen_meson_prog, '--command', 'markdown', ++ '--output', meson.current_build_dir(), ++ '--tool', sdbusplusplus_prog, ++ '--directory', meson.source_root(), ++ 'xyz/openbmc_project/MCTP/Endpoint', ++ ], ++ build_by_default: true, ++) ++ ++subdir('SupportedMessageTypes') ++generated_others += custom_target( ++ 'xyz/openbmc_project/MCTP/SupportedMessageTypes__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/MCTP/SupportedMessageTypes', ++ ], ++ build_by_default: true, ++) ++ +diff --git a/gen/xyz/openbmc_project/meson.build b/gen/xyz/openbmc_project/meson.build +index 3c4750f..e4372b0 100644 +--- a/gen/xyz/openbmc_project/meson.build ++++ b/gen/xyz/openbmc_project/meson.build +@@ -68,6 +68,7 @@ generated_others += custom_target( + subdir('Ipmi') + subdir('Led') + subdir('Logging') ++subdir('MCTP') + subdir('Memory') + subdir('Network') + subdir('Nvme') +-- +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..9d3a8f197 --- /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,441 @@ +From 32d4d17843bcc96a6d17b3d975fc92fac82ef081 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 | 14 ++ + .../ActiveComponentImageSetInfo/meson.build | 14 ++ + .../PLDM/FWU/ActiveComponentInfo/meson.build | 14 ++ + .../FWU/CapabilitiesDuringUpdate/meson.build | 14 ++ + .../ComponentActivationMethods/meson.build | 14 ++ + .../PLDM/FWU/FWUBase/meson.build | 14 ++ + .../PLDM/FWU/IANADescriptor/meson.build | 14 ++ + .../PLDM/FWU/PCIDescriptor/meson.build | 14 ++ + .../PendingComponentImageSetInfo/meson.build | 14 ++ + .../PLDM/FWU/PendingComponentInfo/meson.build | 14 ++ + .../PLDM/FWU/PnPDescriptor/meson.build | 14 ++ + gen/xyz/openbmc_project/PLDM/FWU/meson.build | 166 ++++++++++++++++++ + gen/xyz/openbmc_project/PLDM/meson.build | 1 + + 13 files changed, 321 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 0000000..2ec794d +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/ACPIDescriptor/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ACPIDescriptor__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..d415ec9 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..e2be862 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..62d9894 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..2e379b6 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..149662b +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/FWUBase/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/FWUBase__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..6661829 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/IANADescriptor/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/IANADescriptor__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..00f54e2 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/PCIDescriptor/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PCIDescriptor__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..5349f0f +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..5c44acf +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/PendingComponentInfo/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentInfo__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..d77e841 +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/PnPDescriptor/meson.build +@@ -0,0 +1,14 @@ ++# Generated file; do not modify. ++generated_sources += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PnPDescriptor__cpp'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ '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 0000000..27e89fc +--- /dev/null ++++ b/gen/xyz/openbmc_project/PLDM/FWU/meson.build +@@ -0,0 +1,166 @@ ++# Generated file; do not modify. ++subdir('ACPIDescriptor') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ACPIDescriptor__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/ACPIDescriptor', ++ ], ++ build_by_default: true, ++) ++ ++subdir('ActiveComponentImageSetInfo') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentImageSetInfo', ++ ], ++ build_by_default: true, ++) ++ ++subdir('ActiveComponentInfo') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/ActiveComponentInfo', ++ ], ++ build_by_default: true, ++) ++ ++subdir('CapabilitiesDuringUpdate') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/CapabilitiesDuringUpdate', ++ ], ++ build_by_default: true, ++) ++ ++subdir('ComponentActivationMethods') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/ComponentActivationMethods', ++ ], ++ build_by_default: true, ++) ++ ++subdir('FWUBase') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/FWUBase__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/FWUBase', ++ ], ++ build_by_default: true, ++) ++ ++subdir('IANADescriptor') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/IANADescriptor__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/IANADescriptor', ++ ], ++ build_by_default: true, ++) ++ ++subdir('PCIDescriptor') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PCIDescriptor__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/PCIDescriptor', ++ ], ++ build_by_default: true, ++) ++ ++subdir('PendingComponentImageSetInfo') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentImageSetInfo', ++ ], ++ build_by_default: true, ++) ++ ++subdir('PendingComponentInfo') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentInfo__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/PendingComponentInfo', ++ ], ++ build_by_default: true, ++) ++ ++subdir('PnPDescriptor') ++generated_others += custom_target( ++ 'xyz/openbmc_project/PLDM/FWU/PnPDescriptor__markdown'.underscorify(), ++ input: [ meson.source_root() / '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.source_root(), ++ 'xyz/openbmc_project/PLDM/FWU/PnPDescriptor', ++ ], ++ build_by_default: true, ++) ++ +diff --git a/gen/xyz/openbmc_project/PLDM/meson.build b/gen/xyz/openbmc_project/PLDM/meson.build +index 9087286..02e4234 100644 +--- a/gen/xyz/openbmc_project/PLDM/meson.build ++++ b/gen/xyz/openbmc_project/PLDM/meson.build +@@ -14,6 +14,7 @@ generated_others += custom_target( + build_by_default: true, + ) + ++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-Fix-crash-issue-due-to-throw-undefined-error.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0034-Fix-crash-issue-due-to-throw-undefined-error.patch new file mode 100644 index 000000000..98c3873dd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0034-Fix-crash-issue-due-to-throw-undefined-error.patch @@ -0,0 +1,38 @@ +From df0e7c1414b793a36e34a5875e4196a3a45704a5 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Mon, 21 Dec 2020 23:09:33 +0800 +Subject: [PATCH] Fix crash issue due to throw undefined error + +Below 3 error is used by bios config manager daemon. +xyz.openbmc_project.Common.Error.InvalidArgument +xyz.openbmc_project.BIOSConfig.Common.Error.AttributeNotFound +xyz.openbmc_project.BIOSConfig.Common.Error.AttributeReadOnl + +Tested: +bios config manager daemon could throw these 3 error, +but not crash any more. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + xyz/openbmc_project/BIOSConfig/Manager.interface.yaml | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/xyz/openbmc_project/BIOSConfig/Manager.interface.yaml b/xyz/openbmc_project/BIOSConfig/Manager.interface.yaml +index 225ccaa..98daf3a 100644 +--- a/xyz/openbmc_project/BIOSConfig/Manager.interface.yaml ++++ b/xyz/openbmc_project/BIOSConfig/Manager.interface.yaml +@@ -109,7 +109,10 @@ properties: + ex- { {"QuietBoot",Type.Integer, 0x1}, + { "DdrFreqLimit",Type.String,"2933"} + } +- ++ errors: ++ - xyz.openbmc_project.Common.Error.InvalidArgument ++ - xyz.openbmc_project.BIOSConfig.Common.Error.AttributeNotFound ++ - xyz.openbmc_project.BIOSConfig.Common.Error.AttributeReadOnly + + enumerations: + - name: AttributeType +-- +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..f947b19c0 --- /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 = "6be85da9193d5c529b49fbf9345507e0be2b9477" + +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-Fix-crash-issue-due-to-throw-undefined-error.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..72d991c7e --- /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/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..7e25bb26e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend @@ -0,0 +1,10 @@ +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;nobranch=1" +SRCREV = "1277543ac599de45d15db99d15bd0e89d3653c9b" + +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..85ef5afcf --- /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,215 @@ +From 32d90301c494c1b43f8e1d74ac9e7c1dc2fe486a 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 1/1] 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 | 45 +++++++++++++++++++++++++++++++++++++++++++++ + meson.build | 1 + + meson_options.txt | 3 +++ + static/flash.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- + ubi/flash.cpp | 9 +++------ + 5 files changed, 91 insertions(+), 8 deletions(-) + +diff --git a/activation.cpp b/activation.cpp +index 291ce7c..f76018d 100644 +--- a/activation.cpp ++++ b/activation.cpp +@@ -88,7 +88,50 @@ auto Activation::activation(Activations value) -> Activations + + if (value == softwareServer::Activation::Activations::Activating) + { ++#ifdef FWUPD_SCRIPT ++ if (!activationProgress) ++ { ++ // 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)) ++ { ++ 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) ++ { ++ log<level::ERR>("[Jennifer] 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) +@@ -111,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)) +@@ -175,6 +217,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 62b9aeb..08d6f71 100644 +--- a/meson.build ++++ b/meson.build +@@ -54,6 +54,7 @@ conf.set('MMC_LAYOUT', get_option('bmc-layout').contains('mmc')) + # Configurable features + conf.set('HOST_BIOS_UPGRADE', get_option('host-bios-upgrade').enabled()) + conf.set('WANT_SIGNATURE_VERIFY', get_option('verify-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')) +diff --git a/meson_options.txt b/meson_options.txt +index c9b7b33..4f7e62a 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -22,6 +22,9 @@ option('oe-sdk', type: 'feature', description: 'Enable OE SDK') + option('verify-signature', type: 'feature', + description: 'Enable image 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 9bb2bba..c4ff002 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,46 @@ 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(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 5af2a17..ffa9348 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..49bdc138f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0007-PFR-images-support.patch @@ -0,0 +1,435 @@ +From 030f918b90ea45104bccf68082c2d634c6694238 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> + +--- + activation.cpp | 2 +- + item_updater.cpp | 7 +- + meson.build | 7 +- + meson_options.txt | 3 + + pfr_image_manager.cpp | 217 ++++++++++++++++++++++++++++++++++++++++++ + pfr_image_manager.hpp | 75 +++++++++++++++ + 6 files changed, 306 insertions(+), 5 deletions(-) + create mode 100644 pfr_image_manager.cpp + create mode 100644 pfr_image_manager.hpp + +diff --git a/activation.cpp b/activation.cpp +index bad17b8..3363230 100644 +--- a/activation.cpp ++++ b/activation.cpp +@@ -119,7 +119,7 @@ auto Activation::activation(Activations value) -> Activations + } + else if (activationProgress->progress() == 100) + { +- log<level::ERR>("[Jennifer] progress == 100..."); ++ log<level::INFO>("progress == 100..."); + if (!redundancyPriority) + { + redundancyPriority = +diff --git a/item_updater.cpp b/item_updater.cpp +index df8595c..694975f 100644 +--- a/item_updater.cpp ++++ b/item_updater.cpp +@@ -64,10 +64,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; + } +@@ -399,6 +399,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 +@@ -416,7 +417,7 @@ ItemUpdater::ActivationStatus + return ItemUpdater::ActivationStatus::invalid; + } + } +- ++#endif + return ItemUpdater::ActivationStatus::ready; + } + +@@ -690,8 +691,8 @@ void ItemUpdater::freeSpace(Activation& caller) + // Failed activations don't have priority, assign them a large value + // for sorting purposes. + auto priority = 999; +- if ((iter.second.get()->activation() == +- server::Activation::Activations::Active)&& ++ if ((iter.second.get()->activation() == ++ server::Activation::Activations::Active) && + iter.second->redundancyPriority.get()) + { + priority = iter.second->redundancyPriority.get()->priority(); +diff --git a/meson.build b/meson.build +index 08d6f71..c61d59f 100644 +--- a/meson.build ++++ b/meson.build +@@ -55,6 +55,7 @@ conf.set('MMC_LAYOUT', get_option('bmc-layout').contains('mmc')) + conf.set('HOST_BIOS_UPGRADE', get_option('host-bios-upgrade').enabled()) + conf.set('WANT_SIGNATURE_VERIFY', get_option('verify-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')) +@@ -195,12 +196,16 @@ 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 4f7e62a..1593502 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -25,6 +25,9 @@ option('verify-signature', type: 'feature', + option('fwupd-script', type: 'feature', + description: 'Enable fwupd script support.') + ++option('pfr-update', type: 'feature', ++ description: 'Enable fwupd script 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..242a6ca +--- /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..c6ee6a4 +--- /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/0011-Fix-for-RedudancyPriority-in-item_updater.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0011-Fix-for-RedudancyPriority-in-item_updater.patch new file mode 100644 index 000000000..156e6fe7c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0011-Fix-for-RedudancyPriority-in-item_updater.patch @@ -0,0 +1,36 @@ +From f6022e25d0b47af502522913773e589fcdd1568e Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@intel.com> +Date: Sun, 5 Jul 2020 00:54:57 +0530 +Subject: [PATCH] Fix for RedudancyPriority in item_updater + +This fixes accessing RedudancyPriority property for the +activated image in item_updater. The downloaded image object +is not actually associated with RedudancyPriority before and +after activation. There exists no RedundancyPriority property +for downloaded image, accessing it causing a crash in +item_updater. + +Tested: Tested for coredumps during Seamless firmware update. + +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> + +--- + item_updater.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/item_updater.cpp b/item_updater.cpp +index c3a846d..b299b4d 100644 +--- a/item_updater.cpp ++++ b/item_updater.cpp +@@ -690,8 +690,9 @@ void ItemUpdater::freeSpace(Activation& caller) + // Failed activations don't have priority, assign them a large value + // for sorting purposes. + auto priority = 999; +- if (iter.second.get()->activation() == +- server::Activation::Activations::Active) ++ if ((iter.second.get()->activation() == ++ server::Activation::Activations::Active)&& ++ iter.second->redundancyPriority.get()) + { + priority = iter.second->redundancyPriority.get()->priority(); + } 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..d5d0f513e --- /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 ae3a9616b44677f20b4ca534c3f55ccb478fdf55 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 ccdf594..c738945 100644 +--- a/watch.cpp ++++ b/watch.cpp +@@ -106,6 +106,15 @@ int Watch::callback(sd_event_source* /* s */, int fd, uint32_t revents, + { + log<level::ERR>("Error processing image", + entry("IMAGE=%s", tarballPath.c_str())); ++ std::error_code ec{}; ++ fs::remove_all(tarballPath, ec); ++ if (!ec) ++ { ++ log<level::ERR>( ++ "Unable to remove image on processing failure", ++ entry("ERROR=%s", ec.message().c_str()), ++ entry("IMAGE=%s", tarballPath.c_str())); ++ } + } + } + 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/0015-Fix-delete-image-by-ID-and-inhibit-removal-of-bmc_ac.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0015-Fix-delete-image-by-ID-and-inhibit-removal-of-bmc_ac.patch new file mode 100644 index 000000000..54efbee8c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0015-Fix-delete-image-by-ID-and-inhibit-removal-of-bmc_ac.patch @@ -0,0 +1,153 @@ +From f2dd5e13a0774d8683542798dd96979f9d7a6691 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Tue, 29 Sep 2020 13:38:35 -0700 +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 on the seamless images, the interfaces are + deleted and that the bmc_active interface is not deleted. + +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +--- + item_updater.cpp | 17 +++++++++++------ + pfr_image_manager.cpp | 2 +- + version.cpp | 2 +- + version.hpp | 19 +++++++++++++++---- + 4 files changed, 28 insertions(+), 12 deletions(-) + +diff --git a/item_updater.cpp b/item_updater.cpp +index db255d6..90970d3 100644 +--- a/item_updater.cpp ++++ b/item_updater.cpp +@@ -133,7 +133,7 @@ void ItemUpdater::createActivation(sdbusplus::message::message& msg) + activationState, associations))); + + auto versionPtr = std::make_unique<VersionClass>( +- bus, path, version, purpose, filePath, ++ bus, path, versionId, version, purpose, filePath, + std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); + versionPtr->deleteObject = + std::make_unique<phosphor::software::manager::Delete>(bus, path, +@@ -247,7 +247,7 @@ void ItemUpdater::processBMCImage() + + // Create Version instance for this version. + auto versionPtr = std::make_unique<VersionClass>( +- bus, path, version, purpose, "", ++ bus, path, id, version, purpose, "", + std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); + auto isVersionFunctional = versionPtr->isFunctional(); + if (!isVersionFunctional) +@@ -322,11 +322,11 @@ 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()) + { +- log<level::ERR>("Error: Version is currently running on the BMC. " +- "Unable to remove.", +- entry("VERSIONID=%s", entryId.c_str())); ++ log<level::INFO>("Error: Version is currently running on the BMC. " ++ "Unable to remove.", ++ entry("VERSIONID=%s", entryId.c_str())); + return; + } + } +@@ -669,6 +669,11 @@ 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() == +diff --git a/pfr_image_manager.cpp b/pfr_image_manager.cpp +index 145237e..0c6c3d8 100644 +--- a/pfr_image_manager.cpp ++++ b/pfr_image_manager.cpp +@@ -308,7 +308,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, id, ver, purpose, 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 18f3f4f..e6fd481 100644 +--- a/version.cpp ++++ b/version.cpp +@@ -182,7 +182,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 9cf76da..ae70ea8 100644 +--- a/version.hpp ++++ b/version.hpp +@@ -74,14 +74,15 @@ 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& filePath, eraseFunc callback) : ++ const std::string& extId, const std::string& versionString, ++ VersionPurpose versionPurpose, const std::string& filePath, ++ eraseFunc callback) : + VersionInherit(bus, (objPath).c_str(), true), +- eraseCallback(callback), versionStr(versionString) ++ eraseCallback(callback), extId(extId), versionStr(versionString) + { + // Set properties. + purpose(versionPurpose); +- version(versionString); ++ version(extId); + path(filePath); + // Emit deferred signal. + emit_object_added(); +@@ -134,6 +135,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; + +@@ -143,6 +153,7 @@ class Version : public VersionInherit + private: + /** @brief This Version's version string */ + const std::string versionStr; ++ const std::string extId; + }; + + } // namespace manager +-- +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..c353a09b2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0016-Process-PLDM-image-type.patch @@ -0,0 +1,224 @@ +From aaebe1c3db71b7addbd3f1d1b2b5f83d7cba14e3 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 | 1 + + pfr_image_manager.cpp | 95 +++++++++++++++++++++++++++++++++++++++++-- + pfr_image_manager.hpp | 6 +-- + pldm.hpp | 21 ++++++++++ + 4 files changed, 117 insertions(+), 6 deletions(-) + create mode 100644 pldm.hpp + +diff --git a/item_updater.cpp b/item_updater.cpp +index db255d6..2ed2a3c 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; +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/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..e1c72d161 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend @@ -0,0 +1,26 @@ +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://0011-Fix-for-RedudancyPriority-in-item_updater.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 \ + " + +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..53cec437d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru.bb @@ -0,0 +1,30 @@ +SUMMARY = "Default Fru" +DESCRIPTION = "Builds a default FRU file at runtime based on board ID" + +inherit systemd +inherit cmake + +SYSTEMD_SERVICE_${PN} = "SetBaseboardFru.service" + +S = "${WORKDIR}" +SRC_URI = "file://checkFru.sh \ + file://SetBaseboardFru.service \ + file://mkfru.cpp \ + file://CMakeLists.txt \ + " + +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 -d ${D}${base_libdir}/systemd/system + install -m 0644 ${S}/SetBaseboardFru.service ${D}${base_libdir}/systemd/system +} 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..9227beb20 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fru/default-fru/checkFru.sh @@ -0,0 +1,76 @@ +#!/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" + +if [ -f $fruFile -a -f $prodIDFile ]; then + exit 0 +fi + +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 +} + +BOARD_ID=$(read_id) +if grep -q 'CPU part\s*: 0xb76' /proc/cpuinfo; then + # AST2500 + case $BOARD_ID in + 12) NAME="D50TNP1SB" + PRODID="0x99";; + 40) NAME="CooperCity" + PRODID="0x9d";; + 42) NAME="WilsonCity" + PRODID="0x91";; + 44) NAME="WilsonCityM" + PRODID="0x91";; + 45) NAME="WilsonCity" + PRODID="0x91";; + 60) NAME="M50CYP2SB2U" + PRODID="0x98";; + 62) NAME="WilsonPoint" + PRODID="0x9a";; + *) NAME="S2600WFT" + PRODID="0x7b";; + esac + +elif grep -q 'CPU part\s*: 0xc07' /proc/cpuinfo; then + # AST2600 + case $BOARD_ID in + 62) NAME="ArcherCity" + PRODID="0x9c";; + *) NAME="AST2600EVB" + PRODID="0x00";; + esac + +fi + +if [ -z "$NAME" ]; then + NAME="Unknown" +fi + +if [ ! -e $prodIDFile ] +then + echo $PRODID >$prodIDFile +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/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/obmc-op-control-host%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/host/obmc-op-control-host%.bbappend new file mode 100644 index 000000000..5326680f6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/host/obmc-op-control-host%.bbappend @@ -0,0 +1,6 @@ +SYSTEMD_LINK_${PN}_remove += "../op-start-host@.service:obmc-host-startmin@0.target.requires/op-start-host@0.service" +SYSTEMD_LINK_${PN}_remove += "../op-init-pnor@.service:obmc-host-startmin@0.target.requires/op-init-pnor@0.service" + +FILES_${PN}_remove = "${systemd_unitdir}/system/obmc-host-startmin@0.target.requires" +FILES_${PN}_remove = "${systemd_unitdir}/system/obmc-host-startmin@0.target.requires/op-start-host@0.service" +FILES_${PN}_remove = "${systemd_unitdir}/system/obmc-host-startmin@0.target.requires/op-init-pnor@0.service"
\ No newline at end of file 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..08d867de4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/host/phosphor-host-postd_git.bbappend @@ -0,0 +1 @@ +DEPENDS += " gtest" 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..022ffdc76 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch @@ -0,0 +1,676 @@ +From 98a84ed284fe71e276d425dbe67a447b6fca1eff Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@intel.com> +Date: Wed, 18 Nov 2020 17:14:41 +0530 +Subject: [PATCH 01/10] 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. + +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> + +%% original patch: 0001-Firmware-update-configuration-changes.patch +--- + redfish-core/lib/update_service.hpp | 337 +++++++++++++++++++-- + .../v1/JsonSchemas/OemUpdateService/index.json | 69 +++++ + static/redfish/v1/schema/OemUpdateService_v1.xml | 40 +++ + 3 files changed, 416 insertions(+), 30 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 ddb8b30..399321b 100644 +--- a/redfish-core/lib/update_service.hpp ++++ b/redfish-core/lib/update_service.hpp +@@ -32,6 +32,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"; + + static void cleanUp() + { +@@ -40,27 +51,119 @@ static void cleanUp() + fwUpdateErrorMatcher = nullptr; + } + 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 error_code) { +- if (error_code) ++ [objPath, service, imgTarget{imgUriTargets[0]}]( ++ const boost::system::error_code ec, ++ const crow::openbmc_mapper::GetSubTreeType& subtree) { ++ if (ec || !subtree.size()) + { +- BMCWEB_LOG_DEBUG << "error_code = " << error_code; +- BMCWEB_LOG_DEBUG << "error msg = " << error_code.message(); ++ return; ++ } ++ ++ for (const auto& [invObjPath, invDict] : subtree) ++ { ++ 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<AsyncResp>& asyncResp, ++ const std::vector<std::string> imgUriTargets, + sdbusplus::message::message& m, + const crow::Request& req) + { +@@ -73,22 +176,24 @@ static void softwareInterfaceAdded(const std::shared_ptr<AsyncResp>& asyncResp, + + 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, ++ [objPath, asyncResp, imgTargets{imgUriTargets}, + req](const boost::system::error_code error_code, + const std::vector<std::pair< + std::string, std::vector<std::string>>>& objInfo) { + if (error_code) + { +- BMCWEB_LOG_DEBUG << "error_code = " << error_code; ++ BMCWEB_LOG_DEBUG ++ << "GetSoftwareObject path failed: error_code = " ++ << error_code; + BMCWEB_LOG_DEBUG << "error msg = " + << error_code.message(); + if (asyncResp) +@@ -115,7 +220,7 @@ static void softwareInterfaceAdded(const std::shared_ptr<AsyncResp>& asyncResp, + // 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 = +@@ -245,8 +350,7 @@ static void softwareInterfaceAdded(const std::shared_ptr<AsyncResp>& asyncResp, + "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}); + } + } + } +@@ -255,7 +359,8 @@ static void softwareInterfaceAdded(const std::shared_ptr<AsyncResp>& asyncResp, + // then no asyncResp updates will occur + static void monitorForSoftwareAvailable( + const std::shared_ptr<AsyncResp>& asyncResp, const crow::Request& req, +- const std::string& url, int timeoutTimeSeconds = 10) ++ const std::string& url, const std::vector<std::string>& imgUriTargets, ++ int timeoutTimeSeconds = 10) + { + // Only allow one FW update at a time + if (fwUpdateInProgress != false) +@@ -295,9 +400,10 @@ static void monitorForSoftwareAvailable( + } + }); + +- auto callback = [asyncResp, req](sdbusplus::message::message& m) { ++ auto callback = [asyncResp, imgTargets{imgUriTargets}, ++ req](sdbusplus::message::message& m) { + BMCWEB_LOG_DEBUG << "Match fired"; +- softwareInterfaceAdded(asyncResp, m, req); ++ softwareInterfaceAdded(asyncResp, imgTargets, m, req); + }; + + fwUpdateInProgress = true; +@@ -463,12 +569,15 @@ class UpdateServiceActionsSimpleUpdate : public Node + 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( + nullptr, 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 +@@ -502,7 +611,8 @@ class UpdateServiceActionsSimpleUpdate : public Node + class UpdateService : public Node + { + public: +- UpdateService(App& app) : Node(app, "/redfish/v1/UpdateService/") ++ UpdateService(App& app) : ++ Node(app, "/redfish/v1/UpdateService/"), httpPushUriTargetBusy(false) + { + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, +@@ -514,6 +624,8 @@ class UpdateService : public Node + } + + private: ++ std::vector<std::string> httpPushUriTargets; ++ bool httpPushUriTargetBusy; + void doGet(crow::Response& res, const crow::Request&, + const std::vector<std::string>&) override + { +@@ -524,6 +636,8 @@ class UpdateService : public Node + res.jsonValue["Description"] = "Service for Software Update"; + res.jsonValue["Name"] = "Update Service"; + res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService"; ++ res.jsonValue["HttpPushUriTargets"] = httpPushUriTargets; ++ res.jsonValue["HttpPushUriTargetsBusy"] = httpPushUriTargetBusy; + // UpdateService cannot be disabled + res.jsonValue["ServiceEnabled"] = true; + res.jsonValue["FirmwareInventory"] = { +@@ -573,6 +687,29 @@ class UpdateService : public Node + "/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( ++ [aResp](const boost::system::error_code ec, ++ const std::variant<bool> applyOption) { ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG << "DBUS response error " << ec; ++ messages::internalError(aResp->res); ++ return; ++ } ++ ++ const bool* b = std::get_if<bool>(&applyOption); ++ ++ if (b) ++ { ++ aResp->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"); + } + + void doPatch(crow::Response& res, const crow::Request& req, +@@ -583,12 +720,61 @@ class UpdateService : public Node + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + + std::optional<nlohmann::json> pushUriOptions; +- if (!json_util::readJson(req, res, "HttpPushUriOptions", +- pushUriOptions)) ++ std::optional<std::vector<std::string>> imgTargets; ++ std::optional<bool> imgTargetBusy; ++ std::optional<nlohmann::json> oemProps; ++ ++ if (!json_util::readJson(req, 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, res, "ApplyOptions", ++ applyOptions)) ++ { ++ return; ++ } ++ ++ if (applyOptions) ++ { ++ std::optional<bool> clearConfig; ++ if (!json_util::readJson(*applyOptions, res, "ClearConfig", ++ clearConfig)) ++ { ++ return; ++ } ++ ++ 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; +@@ -653,6 +839,98 @@ class UpdateService : public Node + } + } + } ++ ++ 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) ++ { ++ messages::invalidObject(asyncResp->res, ++ "HttpPushUriTargets"); ++ return; ++ } ++ crow::connections::systemBus->async_method_call( ++ [this, asyncResp, uriTargets{*imgTargets}, ++ targetBusy{*imgTargetBusy}]( ++ const boost::system::error_code ec, ++ const std::vector<std::string> swInvPaths) { ++ if (ec) ++ { ++ return; ++ } ++ ++ bool swInvObjFound = false; ++ for (const std::string& path : swInvPaths) ++ { ++ std::size_t idPos = path.rfind("/"); ++ if ((idPos == std::string::npos) || ++ ((idPos + 1) >= path.size())) ++ { ++ messages::internalError(asyncResp->res); ++ BMCWEB_LOG_DEBUG ++ << "Can't parse firmware ID!!"; ++ return; ++ } ++ std::string swId = path.substr(idPos + 1); ++ ++ if (swId == uriTargets[0]) ++ { ++ swInvObjFound = true; ++ break; ++ } ++ } ++ if (!swInvObjFound) ++ { ++ messages::invalidObject(asyncResp->res, ++ "HttpPushUriTargets"); ++ return; ++ } ++ this->httpPushUriTargetBusy = targetBusy; ++ this->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; ++ } ++ } + } + + void doPost(crow::Response& res, const crow::Request& req, +@@ -663,8 +941,8 @@ class UpdateService : public Node + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + + // Setup callback for when new software detected +- monitorForSoftwareAvailable(asyncResp, req, +- "/redfish/v1/UpdateService"); ++ monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService", ++ httpPushUriTargets); + + std::string filepath( + "/tmp/images/" + +@@ -749,7 +1027,7 @@ class SoftwareInventoryCollection : public Node + "/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}); + } + }; + +@@ -931,7 +1209,7 @@ class SoftwareInventory : public Node + }, + obj.second[0].first, obj.first, + "org.freedesktop.DBus.Properties", "GetAll", +- "xyz.openbmc_project.Software.Version"); ++ versionIntf); + } + if (!found) + { +@@ -952,8 +1230,7 @@ class SoftwareInventory : public Node + "xyz.openbmc_project.ObjectMapper", + "/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"}); ++ static_cast<int32_t>(0), std::array<const char*, 1>{versionIntf}); + } + }; + +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.16.6 + 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..31f9abcd5 --- /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 c61ac0a5cf825193f46c734f1db2f92a72d1f3c9 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 02/10] 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.16.6 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch new file mode 100644 index 000000000..cd4a5317c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch @@ -0,0 +1,219 @@ +From 17d24c7ff9a533ef6ff0e86554840bf5e4a11782 Mon Sep 17 00:00:00 2001 +From: Karol Wachowski <karol.wachowski@intel.com> +Date: Fri, 10 Jul 2020 09:54:06 +0000 +Subject: [PATCH 03/10] 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 | 144 ++++++++++++++++++++++++++++--------- + 1 file changed, 110 insertions(+), 34 deletions(-) + +diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp +index 1336190..1a4a81d 100644 +--- a/redfish-core/lib/virtual_media.hpp ++++ b/redfish-core/lib/virtual_media.hpp +@@ -23,6 +23,8 @@ + // for GetObjectType and ManagedObjectType + #include <account_service.hpp> + ++#include <chrono> ++ + namespace redfish + + { +@@ -121,6 +123,26 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface, + } + } + ++/** ++ * @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. + */ +@@ -811,22 +833,54 @@ class VirtualMediaActionInsertMedia : public Node + } + + 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 ++ { ++ 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"); + } + }; + +@@ -960,38 +1014,60 @@ class VirtualMediaActionEjectMedia : public Node + const std::string& service, const std::string& name, + bool legacy) + { +- +- // Legacy mount requires parameter with image ++ std::string objectPath = "/xyz/openbmc_project/VirtualMedia/"; ++ std::string ifaceName = "xyz.openbmc_project.VirtualMedia"; + 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"); ++ objectPath += "Legacy/"; ++ ifaceName += ".Legacy"; + } +- else // proxy ++ else + { +- 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/Proxy/" + name, +- "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount"); ++ objectPath += "Proxy/"; ++ ifaceName += ".Proxy"; + } ++ objectPath += name; ++ ++ 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; ++ } ++ ++ 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"); + } + }; + +-- +2.16.6 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch new file mode 100644 index 000000000..977a1c6fa --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch @@ -0,0 +1,543 @@ +From f3ae6e96596eadf2a2df4bc723537a47cff13054 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Mon, 19 Oct 2020 13:21:42 +0530 +Subject: [PATCH 04/10] 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 | 367 ++++++++++++++++++------- + redfish-core/include/event_service_manager.hpp | 2 +- + 2 files changed, 264 insertions(+), 105 deletions(-) + +diff --git a/http/http_client.hpp b/http/http_client.hpp +index 5c7b13f..bd1e7b6 100644 +--- a/http/http_client.hpp ++++ b/http/http_client.hpp +@@ -31,12 +31,17 @@ namespace crow + { + + static constexpr uint8_t maxRequestQueueSize = 50; ++static constexpr unsigned int httpReadBodyLimit = 1024; + + enum class ConnState + { + initialized, ++ resolveInProgress, ++ resolveFailed, ++ resolved, + connectInProgress, + connectFailed, ++ sslHandshakeInProgress, + connected, + sendInProgress, + sendFailed, +@@ -50,53 +55,124 @@ enum class ConnState + class HttpClient : public std::enable_shared_from_this<HttpClient> + { + private: ++ boost::asio::ip::tcp::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_buffer buffer; ++ boost::beast::flat_static_buffer<httpReadBodyLimit> buffer; ++ std::optional< ++ boost::beast::http::response_parser<boost::beast::http::string_body>> ++ parser; + boost::beast::http::request<boost::beast::http::string_body> req; +- boost::beast::http::response<boost::beast::http::string_body> res; + boost::asio::ip::tcp::resolver::results_type endpoint; +- std::vector<std::pair<std::string, std::string>> headers; ++ boost::beast::http::fields fields; + std::queue<std::string> requestDataQueue; +- ConnState state; + std::string subId; + std::string host; + std::string port; + std::string uri; ++ bool useSsl; + uint32_t retryCount; + uint32_t maxRetryAttempts; + uint32_t retryIntervalSecs; + std::string retryPolicyAction; + bool runningTimer; ++ ConnState state; ++ ++ void doResolve() ++ { ++ BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":" << port; ++ if (state == ConnState::resolveInProgress) ++ { ++ return; ++ } ++ state = ConnState::resolveInProgress; ++ // TODO: Use async_resolver. boost asio example ++ // code as is crashing with async_resolve(). ++ try ++ { ++ endpoint = resolver.resolve(host, port); ++ } ++ catch (const std::exception& e) ++ { ++ BMCWEB_LOG_ERROR << "Failed to resolve hostname: " << host << " - " ++ << e.what(); ++ state = ConnState::resolveFailed; ++ checkQueue(); ++ return; ++ } ++ state = ConnState::resolved; ++ checkQueue(); ++ } + + void doConnect() + { +- if (state == ConnState::connectInProgress) ++ if (useSsl) ++ { ++ sslConn.emplace(conn, ctx); ++ } ++ ++ if ((state == ConnState::connectInProgress) || ++ (state == ConnState::sslHandshakeInProgress)) + { + return; + } + state = ConnState::connectInProgress; + + BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port; +- // Set a timeout on the operation ++ ++ auto respHandler = ++ [self(shared_from_this())](const boost::beast::error_code ec, ++ const boost::asio::ip::tcp::resolver:: ++ results_type::endpoint_type& ep) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Connect " << ep ++ << " failed: " << ec.message(); ++ self->state = ConnState::connectFailed; ++ self->checkQueue(); ++ return; ++ } ++ BMCWEB_LOG_DEBUG << "Connected to: " << ep; ++ if (self->sslConn) ++ { ++ self->performHandshake(); ++ } ++ else ++ { ++ self->state = ConnState::connected; ++ self->checkQueue(); ++ } ++ }; ++ + conn.expires_after(std::chrono::seconds(30)); +- conn.async_connect(endpoint, [self(shared_from_this())]( +- const boost::beast::error_code& ec, +- const boost::asio::ip::tcp::resolver:: +- results_type::endpoint_type& ep) { +- if (ec) +- { +- BMCWEB_LOG_ERROR << "Connect " << ep +- << " failed: " << ec.message(); +- self->state = ConnState::connectFailed; +- self->checkQueue(); +- return; +- } +- self->state = ConnState::connected; +- BMCWEB_LOG_DEBUG << "Connected to: " << ep; ++ conn.async_connect(endpoint, std::move(respHandler)); ++ } ++ ++ void performHandshake() ++ { ++ if (state == ConnState::sslHandshakeInProgress) ++ { ++ return; ++ } ++ state = ConnState::sslHandshakeInProgress; ++ ++ sslConn->async_handshake( ++ boost::asio::ssl::stream_base::client, ++ [self(shared_from_this())](const boost::beast::error_code ec) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "SSL handshake failed: " ++ << ec.message(); ++ self->doCloseAndCheckQueue(ConnState::connectFailed); ++ return; ++ } ++ self->state = ConnState::connected; ++ BMCWEB_LOG_DEBUG << "SSL Handshake successfull"; + +- self->checkQueue(); +- }); ++ self->checkQueue(); ++ }); + } + + void sendMessage(const std::string& data) +@@ -107,100 +183,167 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + } + state = ConnState::sendInProgress; + +- BMCWEB_LOG_DEBUG << __FUNCTION__ << "(): " << host << ":" << port; ++ BMCWEB_LOG_DEBUG << 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 = {}; ++ for (const auto& field : fields) + { +- req.set(key, value); ++ req.set(field.name_string(), field.value()); + } + req.set(boost::beast::http::field::host, host); ++ req.set(boost::beast::http::field::content_type, "text/plain"); ++ ++ req.version(static_cast<int>(11)); // HTTP 1.1 ++ req.target(uri); ++ req.method(boost::beast::http::verb::post); + req.keep_alive(true); + + 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->doCloseAndCheckQueue(ConnState::sendFailed); ++ return; ++ } ++ BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: " ++ << bytesTransferred; ++ boost::ignore_unused(bytesTransferred); + +- // 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->checkQueue(); +- return; +- } +- BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: " +- << bytesTransferred; +- boost::ignore_unused(bytesTransferred); ++ self->recvMessage(); ++ }; + +- self->recvMessage(); +- }); ++ 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() + { +- // Receive the HTTP response +- boost::beast::http::async_read( +- conn, buffer, res, +- [self(shared_from_this())](const boost::beast::error_code& ec, +- const std::size_t& bytesTransferred) { ++ auto respHandler = [self(shared_from_this())]( ++ const boost::beast::error_code ec, ++ const std::size_t& bytesTransferred) { ++ if (ec && ec != boost::beast::http::error::partial_message) ++ { ++ BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message(); ++ self->doCloseAndCheckQueue(ConnState::recvFailed); ++ return; ++ } ++ BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: " ++ << bytesTransferred; ++ boost::ignore_unused(bytesTransferred); ++ ++ // TODO: check for return status code and perform ++ // retry if fails(Ex: 40x). Take action depending on ++ // retry policy. ++ BMCWEB_LOG_DEBUG << "recvMessage() data: " ++ << self->parser->get().body(); ++ ++ // Send is successful, Lets remove data from queue ++ // check for next request data in queue. ++ self->requestDataQueue.pop(); ++ ++ // Transfer ownership of the response ++ self->parser->release(); ++ ++ // TODO: Implement the keep-alive connections. ++ // Most of the web servers close connection abruptly ++ // and might be reason due to which its observed that ++ // stream_truncated(Next read) or partial_message ++ // errors. So for now, closing connection and re-open ++ // for all cases. ++ self->doCloseAndCheckQueue(ConnState::closed); ++ }; ++ ++ parser.emplace(std::piecewise_construct, std::make_tuple()); ++ parser->body_limit(httpReadBodyLimit); ++ buffer.consume(buffer.size()); ++ ++ 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 doCloseAndCheckQueue(const ConnState setState = ConnState::closed) ++ { ++ if (sslConn) ++ { ++ conn.expires_after(std::chrono::seconds(30)); ++ sslConn->async_shutdown([self = shared_from_this(), ++ setState{std::move(setState)}]( ++ const boost::system::error_code ec) { + if (ec) + { +- BMCWEB_LOG_ERROR << "recvMessage() failed: " +- << ec.message(); +- self->state = ConnState::recvFailed; +- self->checkQueue(); +- 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_ERROR ++ << "doCloseAndCheckQueue(): Connection " ++ "closed by server. "; ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "doCloseAndCheckQueue() failed: " ++ << ec.message(); ++ } + } +- BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: " +- << bytesTransferred; +- boost::ignore_unused(bytesTransferred); +- +- // Discard received data. We are not interested. +- BMCWEB_LOG_DEBUG << "recvMessage() data: " << self->res; +- +- // Send is successful, Lets remove data from queue +- // check for next request data in queue. +- self->requestDataQueue.pop(); +- self->state = ConnState::idle; ++ else ++ { ++ BMCWEB_LOG_DEBUG << "Connection closed gracefully..."; ++ } ++ self->conn.cancel(); ++ self->state = setState; + self->checkQueue(); + }); +- } +- +- void doClose() +- { +- boost::beast::error_code ec; +- conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); +- +- state = ConnState::closed; +- // not_connected happens sometimes so don't bother reporting it. +- if (ec && ec != boost::beast::errc::not_connected) ++ } ++ else + { +- BMCWEB_LOG_ERROR << "shutdown failed: " << ec.message(); +- return; ++ boost::beast::error_code ec; ++ conn.expires_after(std::chrono::seconds(30)); ++ conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ++ ec); ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "doCloseAndCheckQueue() failed: " ++ << ec.message(); ++ } ++ else ++ { ++ BMCWEB_LOG_DEBUG << "Connection closed gracefully..."; ++ } ++ ++ conn.close(); ++ state = setState; ++ checkQueue(); + } +- BMCWEB_LOG_DEBUG << "Connection closed gracefully"; ++ return; + } + + void checkQueue(const bool newRecord = false) + { + if (requestDataQueue.empty()) + { +- // TODO: Having issue in keeping connection alive. So lets close if +- // nothing to be transferred. +- doClose(); +- + BMCWEB_LOG_DEBUG << "requestDataQueue is empty\n"; + return; + } +@@ -232,6 +375,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + } + + if ((state == ConnState::connectFailed) || ++ (state == ConnState::resolveFailed) || + (state == ConnState::sendFailed) || + (state == ConnState::recvFailed)) + { +@@ -256,14 +400,18 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + << " seconds. RetryCount = " << retryCount; + timer.expires_after(std::chrono::seconds(retryIntervalSecs)); + timer.async_wait( +- [self = shared_from_this()](const boost::system::error_code&) { ++ [self = shared_from_this()](boost::system::error_code) { + self->runningTimer = false; + self->connStateCheck(); + }); + return; + } +- // reset retry count. +- retryCount = 0; ++ ++ if (state == ConnState::idle) ++ { ++ // State idle means, previous attempt is successful. ++ retryCount = 0; ++ } + connStateCheck(); + + return; +@@ -273,15 +421,21 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + { + switch (state) + { ++ case ConnState::initialized: ++ case ConnState::resolveFailed: ++ case ConnState::connectFailed: ++ doResolve(); ++ break; + case ConnState::connectInProgress: ++ case ConnState::resolveInProgress: ++ case ConnState::sslHandshakeInProgress: + case ConnState::sendInProgress: + case ConnState::suspended: + case ConnState::terminated: + // do nothing + break; +- case ConnState::initialized: + case ConnState::closed: +- case ConnState::connectFailed: ++ case ConnState::resolved: + case ConnState::sendFailed: + case ConnState::recvFailed: + { +@@ -297,22 +451,22 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + sendMessage(data); + break; + } ++ default: ++ break; + } + } + + public: + explicit HttpClient(boost::asio::io_context& ioc, const std::string& id, + 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), +- retryPolicyAction("TerminateAfterRetries"), runningTimer(false) +- { +- boost::asio::ip::tcp::resolver resolver(ioc); +- endpoint = resolver.resolve(host, port); +- state = ConnState::initialized; +- } ++ const std::string& destUri, ++ const bool inUseSsl = true) : ++ resolver(ioc), ++ conn(ioc), timer(ioc), subId(id), host(destIP), port(destPort), ++ uri(destUri), useSsl(inUseSsl), retryCount(0), maxRetryAttempts(5), ++ retryPolicyAction("TerminateAfterRetries"), runningTimer(false), ++ state(ConnState::initialized) ++ {} + + void sendData(const std::string& data) + { +@@ -337,7 +491,12 @@ class HttpClient : public std::enable_shared_from_this<HttpClient> + void setHeaders( + const std::vector<std::pair<std::string, std::string>>& httpHeaders) + { +- headers = httpHeaders; ++ // Set headers ++ for (const auto& [key, value] : httpHeaders) ++ { ++ // TODO: Validate the header fileds before assign. ++ fields.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 54dafb4..f68ae1d 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -387,7 +387,7 @@ class Subscription + { + conn = std::make_shared<crow::HttpClient>( + crow::connections::systemBus->get_io_context(), id, host, port, +- path); ++ path, (uriProto == "https" ? true : false)); + } + + Subscription(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) : +-- +2.16.6 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Define-Redfish-interface-Registries-Bios.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Define-Redfish-interface-Registries-Bios.patch new file mode 100644 index 000000000..dd2f3483d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Define-Redfish-interface-Registries-Bios.patch @@ -0,0 +1,850 @@ +From 5e3b0c1f8add50acca911a927ba8a1f4864cb315 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> +--- + redfish-core/include/redfish.hpp | 2 + + .../include/registries/bios_registry.hpp | 31 ++ + redfish-core/lib/bios.hpp | 503 ++++++++++++++++++ + redfish-core/lib/message_registries.hpp | 9 +- + 4 files changed, 544 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 5d5eb7b..a8e5cf2 100644 +--- a/redfish-core/include/redfish.hpp ++++ b/redfish-core/include/redfish.hpp +@@ -157,6 +157,8 @@ class RedfishService + nodes.emplace_back(std::make_unique<SystemActionsReset>(app)); + nodes.emplace_back(std::make_unique<SystemResetActionInfo>(app)); + nodes.emplace_back(std::make_unique<BiosService>(app)); ++ nodes.emplace_back(std::make_unique<BiosSettings>(app)); ++ nodes.emplace_back(std::make_unique<BiosAttributeRegistry>(app)); + nodes.emplace_back(std::make_unique<BiosReset>(app)); + #ifdef BMCWEB_ENABLE_VM_NBDPROXY + nodes.emplace_back(std::make_unique<VirtualMedia>(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..88ef782 +--- /dev/null ++++ b/redfish-core/include/registries/bios_registry.hpp +@@ -0,0 +1,31 @@ ++/* ++// 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 ++ ++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", ++}; ++} // namespace redfish::message_registries::bios +\ No newline at end of file +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index 2c31077..5f8c91b 100644 +--- a/redfish-core/lib/bios.hpp ++++ b/redfish-core/lib/bios.hpp +@@ -3,8 +3,140 @@ + #include "node.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. + */ +@@ -35,6 +167,377 @@ class BiosService : public Node + // 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>()); ++ } ++}; ++ ++/** ++ * BiosSettings class supports handle GET/PATCH method for ++ * BIOS configuration pending settings. ++ */ ++class BiosSettings : public Node ++{ ++ public: ++ BiosSettings(App& app) : ++ Node(app, "/redfish/v1/Systems/system/Bios/Settings") ++ { ++ entityPrivileges = {{boost::beast::http::verb::get, {{"Login"}}}}; ++ } ++ ++ private: ++ void doGet(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>&) override ++ { ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ 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. ++ */ ++class BiosAttributeRegistry : public Node ++{ ++ public: ++ BiosAttributeRegistry(App& app) : ++ Node(app, "/redfish/v1/Registries/BiosAttributeRegistry/" ++ "BiosAttributeRegistry") ++ { ++ entityPrivileges = {{boost::beast::http::verb::get, {{"Login"}}}}; ++ } ++ ++ private: ++ void doGet(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>&) override ++ { ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ 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>()); + } + }; + /** +diff --git a/redfish-core/lib/message_registries.hpp b/redfish-core/lib/message_registries.hpp +index 77fc10e..0caf01c 100644 +--- a/redfish-core/lib/message_registries.hpp ++++ b/redfish-core/lib/message_registries.hpp +@@ -18,6 +18,7 @@ + #include "node.hpp" + #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" +@@ -56,11 +57,12 @@ class MessageRegistryFileCollection : public Node + {"@odata.id", "/redfish/v1/Registries"}, + {"Name", "MessageRegistryFile Collection"}, + {"Description", "Collection of MessageRegistryFiles"}, +- {"Members@odata.count", 4}, ++ {"Members@odata.count", 5}, + {"Members", + {{{"@odata.id", "/redfish/v1/Registries/Base"}}, + {{"@odata.id", "/redfish/v1/Registries/TaskEvent"}}, + {{"@odata.id", "/redfish/v1/Registries/ResourceEvent"}}, ++ {{"@odata.id", "/redfish/v1/Registries/BiosAttributeRegistry"}}, + {{"@odata.id", "/redfish/v1/Registries/OpenBMC"}}}}}; + + res.end(); +@@ -118,6 +120,11 @@ class MessageRegistryFile : public Node + header = &message_registries::resource_event::header; + url = message_registries::resource_event::url; + } ++ else if (registry == "BiosAttributeRegistry") ++ { ++ header = &message_registries::bios::header; ++ dmtf.clear(); ++ } + else + { + messages::resourceNotFound( +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0007-BIOS-config-Add-support-for-PATCH-operation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0007-BIOS-config-Add-support-for-PATCH-operation.patch new file mode 100755 index 000000000..18403446d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0007-BIOS-config-Add-support-for-PATCH-operation.patch @@ -0,0 +1,154 @@ +From 8f823ad555b67b228413a0fcb46771d86108dcb3 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Wed, 23 Dec 2020 16:50:45 +0800 +Subject: [PATCH] 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> +--- + redfish-core/lib/bios.hpp | 94 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 93 insertions(+), 1 deletion(-) + +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index 9fe8c6e..e9c8969 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; +@@ -263,7 +286,9 @@ class BiosSettings : public Node + BiosSettings(App& app) : + Node(app, "/redfish/v1/Systems/system/Bios/Settings") + { +- entityPrivileges = {{boost::beast::http::verb::get, {{"Login"}}}}; ++ entityPrivileges = { ++ {boost::beast::http::verb::get, {{"Login"}}}, ++ {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}}; + } + + private: +@@ -361,6 +386,73 @@ class BiosSettings : public Node + "/xyz/openbmc_project/bios_config/manager", + std::array<const char*, 0>()); + } ++ ++ void doPatch(crow::Response& res, const crow::Request& req, ++ const std::vector<std::string>&) override ++ { ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ ++ 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/0008-Add-support-to-ResetBios-action.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0008-Add-support-to-ResetBios-action.patch new file mode 100644 index 000000000..983eb170a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0008-Add-support-to-ResetBios-action.patch @@ -0,0 +1,62 @@ +From 2f5b401bb36be7a1f800bea20fb489a7b2ac6d45 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Wed, 23 Dec 2020 22:47:56 +0800 +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: I5e5fbdd70d4a3ce3b976cc2eb0a7d9a2a3adb124 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> + +--- + redfish-core/lib/bios.hpp | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index 4727ef2..888c511 100644 +--- a/redfish-core/lib/bios.hpp ++++ b/redfish-core/lib/bios.hpp +@@ -663,7 +663,7 @@ class BiosReset : public Node + Node(app, "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios/") + { + entityPrivileges = { +- {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; ++ {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } + + private: +@@ -675,19 +675,23 @@ class BiosReset : public Node + const std::vector<std::string>&) override + { + auto asyncResp = std::make_shared<AsyncResp>(res); +- ++ 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; + } ++ BMCWEB_LOG_DEBUG << "bios reset action is done"; + }, +- "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)); + } + }; + } // namespace redfish +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0009-Add-support-to-ChangePassword-action.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0009-Add-support-to-ChangePassword-action.patch new file mode 100644 index 000000000..c603615f1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0009-Add-support-to-ChangePassword-action.patch @@ -0,0 +1,139 @@ +From 0192a2217eda578ca058cdc3a289ada3ee39e47a Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Wed, 23 Dec 2020 14:41:23 +0800 +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" +} + +Change-Id: I90319a68da0b0a7f9c5cd65a8cb8cf52269a5f52 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + redfish-core/include/redfish.hpp | 1 + + redfish-core/lib/bios.hpp | 70 ++++++++++++++++++++++++++++++++ + 2 files changed, 71 insertions(+) + +diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp +index a8e5cf2..dabf78e 100644 +--- a/redfish-core/include/redfish.hpp ++++ b/redfish-core/include/redfish.hpp +@@ -160,6 +160,7 @@ class RedfishService + nodes.emplace_back(std::make_unique<BiosSettings>(app)); + nodes.emplace_back(std::make_unique<BiosAttributeRegistry>(app)); + nodes.emplace_back(std::make_unique<BiosReset>(app)); ++ nodes.emplace_back(std::make_unique<BiosChangePassword>(app)); + #ifdef BMCWEB_ENABLE_VM_NBDPROXY + nodes.emplace_back(std::make_unique<VirtualMedia>(app)); + nodes.emplace_back(std::make_unique<VirtualMediaCollection>(app)); +diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp +index 7b4306b..52ee356 100644 +--- a/redfish-core/lib/bios.hpp ++++ b/redfish-core/lib/bios.hpp +@@ -186,6 +186,9 @@ class BiosService : public Node + 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, +@@ -674,4 +677,71 @@ class BiosReset : public Node + std::variant<std::string>(resetFlag)); + } + }; ++ ++/** ++ * BiosChangePassword class supports handle POST method for change bios ++ * password. The class retrieves and sends data directly to D-Bus. ++ */ ++class BiosChangePassword : public Node ++{ ++ public: ++ BiosChangePassword(App& app) : ++ Node(app, ++ "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword/") ++ { ++ entityPrivileges = { ++ {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; ++ } ++ ++ private: ++ /** ++ * Function handles POST method request. ++ * Analyzes POST body message before sends Reset request data to D-Bus. ++ */ ++ void doPost(crow::Response& res, const crow::Request& req, ++ const std::vector<std::string>&) override ++ { ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ std::string currentPassword, newPassword, userName; ++ if (!json_util::readJson(req, 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); ++ } ++}; ++ + } // namespace redfish +-- +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..520007d41 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch @@ -0,0 +1,57 @@ +From 01a5c12e350c04f8ed94c7e49a6030a4699eac6e Mon Sep 17 00:00:00 2001 +From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com> +Date: Mon, 28 Dec 2020 18:55:57 +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> +--- + redfish-core/lib/managers.hpp | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp +index 6347caf..c401ca9 100644 +--- a/redfish-core/lib/managers.hpp ++++ b/redfish-core/lib/managers.hpp +@@ -1767,6 +1767,12 @@ class Manager : public Node + res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; + res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = {"IPMI", + "SSH"}; ++ // Fill in CommandShell info ++ res.jsonValue["CommandShell"]["ServiceEnabled"] = true; ++ res.jsonValue["CommandShell"]["MaxConcurrentSessions"] = 4; ++ res.jsonValue["CommandShell"]["ConnectTypesSupported"] = {"SSH", ++ "IPMI"}; ++ + #ifdef BMCWEB_ENABLE_KVM + // Fill in GraphicalConsole info + res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0034-recommended-fixes-by-crypto-review-team.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0034-recommended-fixes-by-crypto-review-team.patch new file mode 100644 index 000000000..f3235c7cf --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0034-recommended-fixes-by-crypto-review-team.patch @@ -0,0 +1,75 @@ +From a170675fafc1ee8bfee502672e65be9ad379a3d1 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 deb3a76..8063858 100644 +--- a/include/ssl_key_handler.hpp ++++ b/include/ssl_key_handler.hpp +@@ -326,31 +326,34 @@ inline std::shared_ptr<boost::asio::ssl::context> + mSslContext->use_private_key_file(ssl_pem_file, + 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/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/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch new file mode 100644 index 000000000..b1334a420 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch @@ -0,0 +1,717 @@ +From c7fce288802ece4a6e1ff71ee060a44e0b8fe992 Mon Sep 17 00:00:00 2001 +From: "Wludzik, Jozef" <jozef.wludzik@intel.com> +Date: Mon, 27 Apr 2020 17:24:15 +0200 +Subject: [PATCH 1/4] Redfish TelemetryService schema implementation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Added TelemetryService, MetricReports, MetricReportCollection, +MetricReportDefinition and MetricReportDefinitionCollection schemas +with GET method support. Added TelemetryService URI to root service. +Implemented communication with backend - Telemetry. +Added schemes attributes that are supported by Telemetry service +design. User is able to fetch basic information about reports if +Telemetry service is present in OpenBMC. +Added util function that converts decimal value into duration format +that is described by ISO 8601 and Redfish specification. + +Tested: + - Succesfully passed RedfishServiceValidator.py + - Verified DBus method calls to Telemetry service + - Verified all possible pages that are displayed to user when: + - Reports are fully defined in Telemetry + - Reports are partially available in Telemetry + - Telemetry is disabled + - Verified time_utils::toDurationString() output + +Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com> +Signed-off-by: Adrian Ambrożewicz <adrian.ambrozewicz@linux.intel.com> +Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Change-Id: Ie6b0b49f4ef5eeaef07d1209b6c349270c04d570 +--- + redfish-core/include/redfish.hpp | 10 ++ + redfish-core/include/utils/telemetry_utils.hpp | 71 ++++++++++ + redfish-core/include/utils/time_utils.hpp | 78 +++++++++++ + redfish-core/lib/metric_report.hpp | 162 +++++++++++++++++++++ + redfish-core/lib/metric_report_definition.hpp | 186 +++++++++++++++++++++++++ + redfish-core/lib/service_root.hpp | 2 + + redfish-core/lib/telemetry_service.hpp | 93 +++++++++++++ + 7 files changed, 602 insertions(+) + create mode 100644 redfish-core/include/utils/telemetry_utils.hpp + create mode 100644 redfish-core/include/utils/time_utils.hpp + create mode 100644 redfish-core/lib/metric_report.hpp + create mode 100644 redfish-core/lib/metric_report_definition.hpp + create mode 100644 redfish-core/lib/telemetry_service.hpp + +diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp +index 54d5d0e..2587b37 100644 +--- a/redfish-core/include/redfish.hpp ++++ b/redfish-core/include/redfish.hpp +@@ -25,6 +25,8 @@ + #include "../lib/managers.hpp" + #include "../lib/memory.hpp" + #include "../lib/message_registries.hpp" ++#include "../lib/metric_report.hpp" ++#include "../lib/metric_report_definition.hpp" + #include "../lib/network_protocol.hpp" + #include "../lib/pcie.hpp" + #include "../lib/power.hpp" +@@ -36,6 +38,7 @@ + #include "../lib/storage.hpp" + #include "../lib/systems.hpp" + #include "../lib/task.hpp" ++#include "../lib/telemetry_service.hpp" + #include "../lib/thermal.hpp" + #include "../lib/update_service.hpp" + #ifdef BMCWEB_ENABLE_VM_NBDPROXY +@@ -207,6 +210,13 @@ class RedfishService + nodes.emplace_back(std::make_unique<HypervisorInterface>(app)); + nodes.emplace_back(std::make_unique<HypervisorSystem>(app)); + ++ nodes.emplace_back(std::make_unique<TelemetryService>(app)); ++ nodes.emplace_back( ++ std::make_unique<MetricReportDefinitionCollection>(app)); ++ nodes.emplace_back(std::make_unique<MetricReportDefinition>(app)); ++ nodes.emplace_back(std::make_unique<MetricReportCollection>(app)); ++ nodes.emplace_back(std::make_unique<MetricReport>(app)); ++ + for (const auto& node : nodes) + { + node->initPrivileges(); +diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp +new file mode 100644 +index 0000000..8caee2d +--- /dev/null ++++ b/redfish-core/include/utils/telemetry_utils.hpp +@@ -0,0 +1,71 @@ ++#pragma once ++ ++namespace redfish ++{ ++ ++namespace telemetry ++{ ++ ++constexpr const char* service = "xyz.openbmc_project.Telemetry"; ++constexpr const char* reportInterface = "xyz.openbmc_project.Telemetry.Report"; ++constexpr const char* metricReportDefinitionUri = ++ "/redfish/v1/TelemetryService/MetricReportDefinitions/"; ++constexpr const char* metricReportUri = ++ "/redfish/v1/TelemetryService/MetricReports/"; ++ ++inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp, ++ const std::string& uri) ++{ ++ const std::array<const char*, 1> interfaces = {reportInterface}; ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, uri](const boost::system::error_code ec, ++ const std::vector<std::string>& reportPaths) { ++ if (ec) ++ { ++ asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); ++ asyncResp->res.jsonValue["Members@odata.count"] = 0; ++ return; ++ } ++ ++ nlohmann::json& members = asyncResp->res.jsonValue["Members"]; ++ members = nlohmann::json::array(); ++ ++ for (const std::string& path : reportPaths) ++ { ++ std::size_t pos = path.rfind('/'); ++ if (pos == std::string::npos) ++ { ++ BMCWEB_LOG_ERROR << "Failed to find '/' in " << path; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ if (path.size() <= (pos + 1)) ++ { ++ BMCWEB_LOG_ERROR << "Failed to parse path " << path; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ members.push_back({{"@odata.id", uri + path.substr(pos + 1)}}); ++ } ++ ++ asyncResp->res.jsonValue["Members@odata.count"] = members.size(); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", ++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService", 1, ++ interfaces); ++} ++ ++inline std::string getDbusReportPath(const std::string& id) ++{ ++ std::string path = ++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id; ++ dbus::utility::escapePathForDbus(path); ++ return path; ++} ++ ++} // namespace telemetry ++} // namespace redfish +diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp +new file mode 100644 +index 0000000..dd4ea75 +--- /dev/null ++++ b/redfish-core/include/utils/time_utils.hpp +@@ -0,0 +1,78 @@ ++#pragma once ++ ++#include <chrono> ++#include <string> ++ ++namespace redfish ++{ ++ ++namespace time_utils ++{ ++ ++namespace details ++{ ++ ++inline void leftZeroPadding(std::string& str, const std::size_t padding) ++{ ++ if (str.size() < padding) ++ { ++ str.insert(0, padding - str.size(), '0'); ++ } ++} ++} // namespace details ++ ++/** ++ * @brief Convert time value into duration format that is based on ISO 8601. ++ * Example output: "P12DT1M5.5S" ++ * Ref: Redfish Specification, Section 9.4.4. Duration values ++ */ ++std::string toDurationString(std::chrono::milliseconds ms) ++{ ++ if (ms < std::chrono::milliseconds::zero()) ++ { ++ return ""; ++ } ++ ++ std::string fmt; ++ fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS")); ++ ++ using Days = std::chrono::duration<long, std::ratio<24 * 60 * 60>>; ++ Days days = std::chrono::floor<Days>(ms); ++ ms -= days; ++ ++ std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms); ++ ms -= hours; ++ ++ std::chrono::minutes minutes = std::chrono::floor<std::chrono::minutes>(ms); ++ ms -= minutes; ++ ++ std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms); ++ ms -= seconds; ++ ++ fmt = "P"; ++ if (days.count() > 0) ++ { ++ fmt += std::to_string(days.count()) + "D"; ++ } ++ fmt += "T"; ++ if (hours.count() > 0) ++ { ++ fmt += std::to_string(hours.count()) + "H"; ++ } ++ if (minutes.count() > 0) ++ { ++ fmt += std::to_string(minutes.count()) + "M"; ++ } ++ if (seconds.count() != 0 || ms.count() != 0) ++ { ++ fmt += std::to_string(seconds.count()) + "."; ++ std::string msStr = std::to_string(ms.count()); ++ details::leftZeroPadding(msStr, 3); ++ fmt += msStr + "S"; ++ } ++ ++ return fmt; ++} ++ ++} // namespace time_utils ++} // namespace redfish +diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp +new file mode 100644 +index 0000000..050304c +--- /dev/null ++++ b/redfish-core/lib/metric_report.hpp +@@ -0,0 +1,162 @@ ++#pragma once ++ ++#include "node.hpp" ++#include "utils/telemetry_utils.hpp" ++ ++namespace redfish ++{ ++ ++class MetricReportCollection : public Node ++{ ++ public: ++ MetricReportCollection(App& app) : ++ Node(app, "/redfish/v1/TelemetryService/MetricReports/") ++ { ++ entityPrivileges = { ++ {boost::beast::http::verb::get, {{"Login"}}}, ++ {boost::beast::http::verb::head, {{"Login"}}}, ++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::put, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; ++ } ++ ++ private: ++ void doGet(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>&) override ++ { ++ res.jsonValue["@odata.type"] = ++ "#MetricReportCollection.MetricReportCollection"; ++ res.jsonValue["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricReports"; ++ res.jsonValue["Name"] = "Metric Report Collection"; ++ ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ telemetry::getReportCollection(asyncResp, telemetry::metricReportUri); ++ } ++}; ++ ++class MetricReport : public Node ++{ ++ public: ++ MetricReport(App& app) : ++ Node(app, "/redfish/v1/TelemetryService/MetricReports/<str>/", ++ std::string()) ++ { ++ entityPrivileges = { ++ {boost::beast::http::verb::get, {{"Login"}}}, ++ {boost::beast::http::verb::head, {{"Login"}}}, ++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::put, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; ++ } ++ ++ private: ++ void doGet(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>& params) override ++ { ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ ++ if (params.size() != 1) ++ { ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ const std::string& id = params[0]; ++ const std::string reportPath = telemetry::getDbusReportPath(id); ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, id, reportPath](const boost::system::error_code& ec) { ++ if (ec.value() == EBADR) ++ { ++ messages::resourceNotFound(asyncResp->res, schemaType, id); ++ return; ++ } ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, ++ id](const boost::system::error_code ec, ++ const std::variant<TimestampReadings>& ret) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ fillReport(asyncResp, id, ret); ++ }, ++ telemetry::service, reportPath, ++ "org.freedesktop.DBus.Properties", "Get", ++ telemetry::reportInterface, "Readings"); ++ }, ++ telemetry::service, reportPath, telemetry::reportInterface, ++ "Update"); ++ } ++ ++ using Readings = ++ std::vector<std::tuple<std::string, std::string, double, uint64_t>>; ++ using TimestampReadings = std::tuple<uint64_t, Readings>; ++ ++ static nlohmann::json toMetricValues(const Readings& readings) ++ { ++ nlohmann::json metricValues = nlohmann::json::array_t(); ++ ++ for (auto& [id, metadata, sensorValue, timestamp] : readings) ++ { ++ nlohmann::json metadataJson = nlohmann::json::parse(metadata); ++ metricValues.push_back({ ++ {"MetricId", id}, ++ {"MetricDefinition", metadataJson.contains("MetricDefinition") ++ ? metadataJson["MetricDefinition"] ++ : nlohmann::json()}, ++ {"MetricProperty", metadataJson.contains("MetricProperty") ++ ? metadataJson["MetricProperty"] ++ : nlohmann::json()}, ++ {"MetricValue", std::to_string(sensorValue)}, ++ {"Timestamp", ++ crow::utility::getDateTime(static_cast<time_t>(timestamp))}, ++ }); ++ } ++ ++ return metricValues; ++ } ++ ++ static void fillReport(const std::shared_ptr<AsyncResp>& asyncResp, ++ const std::string& id, ++ const std::variant<TimestampReadings>& var) ++ { ++ asyncResp->res.jsonValue["@odata.type"] = schemaType; ++ asyncResp->res.jsonValue["@odata.id"] = telemetry::metricReportUri + id; ++ asyncResp->res.jsonValue["Id"] = id; ++ asyncResp->res.jsonValue["Name"] = id; ++ asyncResp->res.jsonValue["MetricReportDefinition"]["@odata.id"] = ++ telemetry::metricReportDefinitionUri + id; ++ ++ const TimestampReadings* timestampReadings = ++ std::get_if<TimestampReadings>(&var); ++ if (!timestampReadings) ++ { ++ BMCWEB_LOG_ERROR << "Property type mismatch or property is missing"; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ const auto& [timestamp, readings] = *timestampReadings; ++ asyncResp->res.jsonValue["Timestamp"] = ++ crow::utility::getDateTime(static_cast<time_t>(timestamp)); ++ asyncResp->res.jsonValue["MetricValues"] = toMetricValues(readings); ++ } ++ ++ static constexpr const char* schemaType = ++ "#MetricReport.v1_3_0.MetricReport"; ++}; ++} // namespace redfish +diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp +new file mode 100644 +index 0000000..48c56e6 +--- /dev/null ++++ b/redfish-core/lib/metric_report_definition.hpp +@@ -0,0 +1,186 @@ ++#pragma once ++ ++#include "node.hpp" ++#include "utils/telemetry_utils.hpp" ++#include "utils/time_utils.hpp" ++ ++#include <tuple> ++#include <variant> ++ ++namespace redfish ++{ ++ ++class MetricReportDefinitionCollection : public Node ++{ ++ public: ++ MetricReportDefinitionCollection(App& app) : ++ Node(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") ++ { ++ entityPrivileges = { ++ {boost::beast::http::verb::get, {{"Login"}}}, ++ {boost::beast::http::verb::head, {{"Login"}}}, ++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::put, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; ++ } ++ ++ private: ++ void doGet(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>&) override ++ { ++ res.jsonValue["@odata.type"] = "#MetricReportDefinitionCollection." ++ "MetricReportDefinitionCollection"; ++ res.jsonValue["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricReportDefinitions"; ++ res.jsonValue["Name"] = "Metric Definition Collection"; ++ ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ telemetry::getReportCollection(asyncResp, ++ telemetry::metricReportDefinitionUri); ++ } ++}; ++ ++class MetricReportDefinition : public Node ++{ ++ public: ++ MetricReportDefinition(App& app) : ++ Node(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/", ++ std::string()) ++ { ++ entityPrivileges = { ++ {boost::beast::http::verb::get, {{"Login"}}}, ++ {boost::beast::http::verb::head, {{"Login"}}}, ++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::put, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; ++ } ++ ++ private: ++ void doGet(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>& params) override ++ { ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ ++ if (params.size() != 1) ++ { ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ const std::string& id = params[0]; ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, ++ id](const boost::system::error_code ec, ++ const std::vector<std::pair< ++ std::string, std::variant<bool, ReadingParameters, ++ std::string, uint64_t>>>& ret) { ++ if (ec.value() == EBADR) ++ { ++ messages::resourceNotFound(asyncResp->res, schemaType, id); ++ return; ++ } ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ fillReportDefinition(asyncResp, id, ret); ++ }, ++ telemetry::service, telemetry::getDbusReportPath(id), ++ "org.freedesktop.DBus.Properties", "GetAll", ++ telemetry::reportInterface); ++ } ++ ++ using ReadingParameters = ++ std::vector<std::tuple<std::vector<sdbusplus::message::object_path>, ++ std::string, std::string, std::string>>; ++ ++ static void fillReportDefinition( ++ const std::shared_ptr<AsyncResp>& asyncResp, const std::string& id, ++ const std::vector< ++ std::pair<std::string, std::variant<bool, ReadingParameters, ++ std::string, uint64_t>>>& ret) ++ { ++ asyncResp->res.jsonValue["@odata.type"] = schemaType; ++ 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) ++ { ++ if (key == "EmitsReadingsUpdate") ++ { ++ emitsReadingsUpdate = std::get_if<bool>(&var); ++ } ++ else if (key == "LogToMetricReportsCollection") ++ { ++ logToMetricReportsCollection = std::get_if<bool>(&var); ++ } ++ 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") ++ { ++ interval = std::get_if<uint64_t>(&var); ++ } ++ } ++ 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"); ++ } ++ if (*logToMetricReportsCollection) ++ { ++ redfishReportActions.emplace_back("LogToMetricReportsCollection"); ++ } ++ ++ nlohmann::json metrics = nlohmann::json::array(); ++ for (auto& [sensorPaths, operationType, id, metadata] : *readingParams) ++ { ++ nlohmann::json metadataJson = nlohmann::json::parse(metadata); ++ metrics.push_back({ ++ {"MetricId", id}, ++ {"MetricProperties", metadataJson.contains("MetricProperties") ++ ? metadataJson["MetricProperties"] ++ : nlohmann::json()}, ++ }); ++ } ++ 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)); ++ } ++ ++ static constexpr const char* schemaType = ++ "#MetricReportDefinition.v1_3_0.MetricReportDefinition"; ++}; ++} // namespace redfish +diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp +index 629280c..3df5ec5 100644 +--- a/redfish-core/lib/service_root.hpp ++++ b/redfish-core/lib/service_root.hpp +@@ -68,6 +68,8 @@ class ServiceRoot : public Node + res.jsonValue["Tasks"] = {{"@odata.id", "/redfish/v1/TaskService"}}; + res.jsonValue["EventService"] = { + {"@odata.id", "/redfish/v1/EventService"}}; ++ res.jsonValue["TelemetryService"] = { ++ {"@odata.id", "/redfish/v1/TelemetryService"}}; + res.end(); + } + +diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp +new file mode 100644 +index 0000000..a6acc34 +--- /dev/null ++++ b/redfish-core/lib/telemetry_service.hpp +@@ -0,0 +1,93 @@ ++#pragma once ++ ++#include "node.hpp" ++#include "utils/telemetry_utils.hpp" ++ ++#include <variant> ++ ++namespace redfish ++{ ++ ++class TelemetryService : public Node ++{ ++ public: ++ TelemetryService(App& app) : Node(app, "/redfish/v1/TelemetryService/") ++ { ++ entityPrivileges = { ++ {boost::beast::http::verb::get, {{"Login"}}}, ++ {boost::beast::http::verb::head, {{"Login"}}}, ++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::put, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; ++ } ++ ++ private: ++ void doGet(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>&) override ++ { ++ res.jsonValue["@odata.type"] = ++ "#TelemetryService.v1_2_1.TelemetryService"; ++ res.jsonValue["@odata.id"] = "/redfish/v1/TelemetryService"; ++ res.jsonValue["Id"] = "TelemetryService"; ++ res.jsonValue["Name"] = "Telemetry Service"; ++ ++ res.jsonValue["LogService"]["@odata.id"] = ++ "/redfish/v1/Managers/bmc/LogServices/Journal"; ++ res.jsonValue["MetricReportDefinitions"]["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricReportDefinitions"; ++ res.jsonValue["MetricReports"]["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricReports"; ++ ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ crow::connections::systemBus->async_method_call( ++ [asyncResp]( ++ const boost::system::error_code ec, ++ const std::vector<std::pair< ++ std::string, std::variant<uint32_t, uint64_t>>>& ret) { ++ if (ec == boost::system::errc::host_unreachable) ++ { ++ asyncResp->res.jsonValue["Status"]["State"] = "Absent"; ++ return; ++ } ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; ++ ++ const size_t* maxReports = nullptr; ++ const uint64_t* minInterval = nullptr; ++ for (const auto& [key, var] : ret) ++ { ++ if (key == "MaxReports") ++ { ++ maxReports = std::get_if<size_t>(&var); ++ } ++ else if (key == "MinInterval") ++ { ++ minInterval = std::get_if<uint64_t>(&var); ++ } ++ } ++ if (!maxReports || !minInterval) ++ { ++ BMCWEB_LOG_ERROR ++ << "Property type mismatch or property is missing"; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ asyncResp->res.jsonValue["MaxReports"] = *maxReports; ++ asyncResp->res.jsonValue["MinCollectionInterval"] = ++ time_utils::toDurationString(std::chrono::milliseconds( ++ static_cast<time_t>(*minInterval))); ++ }, ++ telemetry::service, "/xyz/openbmc_project/Telemetry/Reports", ++ "org.freedesktop.DBus.Properties", "GetAll", ++ "xyz.openbmc_project.Telemetry.ReportManager"); ++ } ++}; ++} // namespace redfish +-- +2.16.6 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch new file mode 100644 index 000000000..b04a72c9f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch @@ -0,0 +1,683 @@ +From 0784af276b72e5df9c545d83bc989833ac2935c4 Mon Sep 17 00:00:00 2001 +From: "Wludzik, Jozef" <jozef.wludzik@intel.com> +Date: Mon, 18 May 2020 11:56:57 +0200 +Subject: [PATCH 2/4] Add POST and DELETE in MetricReportDefinitions + +Added POST action in MetricReportDefinitions node to allow user +to add new MetricReportDefinition. Using minimal set of +MetricReportDefinition parameters from user bmcweb converts it to +DBus call "AddReport" to Telemetry that serves as a backend +for Redfish TelemetryService. +Added DELETE request in MetricReportDefinitions node to allow user +to remove report from Telemetry. +Added conversion from string that represents duration format into +its numeric equivalent. + +Tested: + - Succesfully passed RedfishServiceValidator.py + - Validated good cases with different parameters for POST action + - Validated bad cases with different parameters for POST action + - Verified time_utils::fromDurationString() + - Verified that reports are removed on DELETE request + +Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com> +Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Change-Id: I2fed96848594451e22fde686f8c066d7770cc65a +--- + redfish-core/include/utils/telemetry_utils.hpp | 5 +- + redfish-core/include/utils/time_utils.hpp | 145 +++++++++- + redfish-core/lib/metric_report_definition.hpp | 382 ++++++++++++++++++++++++- + 3 files changed, 516 insertions(+), 16 deletions(-) + +diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp +index 8caee2d..acb739d 100644 +--- a/redfish-core/include/utils/telemetry_utils.hpp ++++ b/redfish-core/include/utils/telemetry_utils.hpp +@@ -12,6 +12,8 @@ constexpr const char* metricReportDefinitionUri = + "/redfish/v1/TelemetryService/MetricReportDefinitions/"; + constexpr const char* metricReportUri = + "/redfish/v1/TelemetryService/MetricReports/"; ++constexpr const char* reportDir = ++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/"; + + inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp, + const std::string& uri) +@@ -61,8 +63,7 @@ inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp, + + inline std::string getDbusReportPath(const std::string& id) + { +- std::string path = +- "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id; ++ std::string path = reportDir + id; + dbus::utility::escapePathForDbus(path); + return path; + } +diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp +index dd4ea75..d8985ab 100644 +--- a/redfish-core/include/utils/time_utils.hpp ++++ b/redfish-core/include/utils/time_utils.hpp +@@ -1,7 +1,12 @@ + #pragma once + ++#include "logging.hpp" ++ ++#include <charconv> + #include <chrono> ++#include <optional> + #include <string> ++#include <system_error> + + namespace redfish + { +@@ -12,6 +17,8 @@ namespace time_utils + namespace details + { + ++using Days = std::chrono::duration<long long, std::ratio<24 * 60 * 60>>; ++ + inline void leftZeroPadding(std::string& str, const std::size_t padding) + { + if (str.size() < padding) +@@ -19,8 +26,143 @@ inline void leftZeroPadding(std::string& str, const std::size_t padding) + str.insert(0, padding - str.size(), '0'); + } + } ++ ++inline bool fromChars(const char* start, const char* end, ++ std::chrono::milliseconds::rep& val) ++{ ++ auto [ptr, ec] = std::from_chars(start, end, val); ++ if (ptr != end) ++ { ++ BMCWEB_LOG_ERROR ++ << "Failed to convert string to decimal because of unexpected sign"; ++ return false; ++ } ++ if (ec != std::errc()) ++ { ++ BMCWEB_LOG_ERROR << "Failed to convert string to decimal with err: " ++ << static_cast<int>(ec) << "(" ++ << std::make_error_code(ec).message() << ")"; ++ return false; ++ } ++ return true; ++} ++ ++template <typename T> ++bool fromDurationItem(std::string_view& fmt, const char postfix, ++ std::chrono::milliseconds& out) ++{ ++ const size_t pos = fmt.find(postfix); ++ if (pos == std::string::npos) ++ { ++ return true; ++ } ++ if ((pos + 1U) > fmt.size()) ++ { ++ return false; ++ } ++ ++ std::chrono::milliseconds::rep v = 0; ++ if constexpr (std::is_same_v<T, std::chrono::milliseconds>) ++ { ++ std::string str(fmt.data(), std::min<size_t>(pos, 3U)); ++ while (str.size() < 3U) ++ { ++ str += '0'; ++ } ++ if (!fromChars(str.data(), str.data() + str.size(), v)) ++ { ++ return false; ++ } ++ } ++ else ++ { ++ if (!fromChars(fmt.data(), fmt.data() + pos, v)) ++ { ++ return false; ++ } ++ } ++ ++ out += T(v); ++ if (out < T(v) || ++ std::chrono::duration_cast<T>(std::chrono::milliseconds::max()) ++ .count() < v) ++ { ++ return false; ++ } ++ ++ fmt.remove_prefix(pos + 1U); ++ return true; ++} + } // namespace details + ++/** ++ * @brief Convert string that represents value in Duration Format to its numeric ++ * equivalent. ++ */ ++std::optional<std::chrono::milliseconds> ++ fromDurationString(const std::string& str) ++{ ++ std::chrono::milliseconds out = std::chrono::milliseconds::zero(); ++ std::string_view v = str; ++ ++ if (v.empty()) ++ { ++ return out; ++ } ++ if (v.front() != 'P') ++ { ++ BMCWEB_LOG_ERROR << "Invalid duration format: " << str; ++ return std::nullopt; ++ } ++ ++ v.remove_prefix(1); ++ if (!details::fromDurationItem<details::Days>(v, 'D', out)) ++ { ++ BMCWEB_LOG_ERROR << "Invalid duration format: " << str; ++ return std::nullopt; ++ } ++ ++ if (v.empty()) ++ { ++ return out; ++ } ++ if (v.front() != 'T') ++ { ++ BMCWEB_LOG_ERROR << "Invalid duration format: " << str; ++ return std::nullopt; ++ } ++ ++ v.remove_prefix(1); ++ if (!details::fromDurationItem<std::chrono::hours>(v, 'H', out) || ++ !details::fromDurationItem<std::chrono::minutes>(v, 'M', out)) ++ { ++ BMCWEB_LOG_ERROR << "Invalid duration format: " << str; ++ return std::nullopt; ++ } ++ ++ if (v.find('.') != std::string::npos && v.find('S') != std::string::npos) ++ { ++ if (!details::fromDurationItem<std::chrono::seconds>(v, '.', out) || ++ !details::fromDurationItem<std::chrono::milliseconds>(v, 'S', out)) ++ { ++ BMCWEB_LOG_ERROR << "Invalid duration format: " << str; ++ return std::nullopt; ++ } ++ } ++ else if (!details::fromDurationItem<std::chrono::seconds>(v, 'S', out)) ++ { ++ BMCWEB_LOG_ERROR << "Invalid duration format: " << str; ++ return std::nullopt; ++ } ++ ++ if (!v.empty()) ++ { ++ BMCWEB_LOG_ERROR << "Invalid duration format: " << str; ++ return std::nullopt; ++ } ++ return out; ++} ++ + /** + * @brief Convert time value into duration format that is based on ISO 8601. + * Example output: "P12DT1M5.5S" +@@ -36,8 +178,7 @@ std::string toDurationString(std::chrono::milliseconds ms) + std::string fmt; + fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS")); + +- using Days = std::chrono::duration<long, std::ratio<24 * 60 * 60>>; +- Days days = std::chrono::floor<Days>(ms); ++ details::Days days = std::chrono::floor<details::Days>(ms); + ms -= days; + + std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms); +diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp +index 48c56e6..d5a540d 100644 +--- a/redfish-core/lib/metric_report_definition.hpp ++++ b/redfish-core/lib/metric_report_definition.hpp +@@ -1,15 +1,26 @@ + #pragma once + + #include "node.hpp" ++#include "sensors.hpp" + #include "utils/telemetry_utils.hpp" + #include "utils/time_utils.hpp" + ++#include <boost/container/flat_map.hpp> ++ + #include <tuple> + #include <variant> + + namespace redfish + { + ++namespace telemetry ++{ ++ ++using ReadingParameters = ++ std::vector<std::tuple<std::vector<sdbusplus::message::object_path>, ++ std::string, std::string, std::string>>; ++} // namespace telemetry ++ + class MetricReportDefinitionCollection : public Node + { + public: +@@ -39,6 +50,318 @@ class MetricReportDefinitionCollection : public Node + telemetry::getReportCollection(asyncResp, + telemetry::metricReportDefinitionUri); + } ++ ++ struct AddReportArgs ++ { ++ std::string name; ++ std::string reportingType; ++ bool emitsReadingsUpdate = false; ++ bool logToMetricReportsCollection = false; ++ uint64_t interval = 0; ++ std::vector<std::pair<std::string, std::vector<std::string>>> metrics; ++ }; ++ ++ void doPost(crow::Response& res, const crow::Request& req, ++ const std::vector<std::string>&) override ++ { ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ AddReportArgs args; ++ if (!getUserParameters(res, req, args)) ++ { ++ return; ++ } ++ ++ boost::container::flat_set<std::pair<std::string, std::string>> ++ chassisSensors; ++ if (!getChassisSensorNode(asyncResp, args.metrics, chassisSensors)) ++ { ++ return; ++ } ++ ++ auto addReportReq = ++ std::make_shared<AddReport>(std::move(args), asyncResp); ++ for (const auto& [chassis, sensorType] : chassisSensors) ++ { ++ retrieveUriToDbusMap( ++ chassis, sensorType, ++ [asyncResp, addReportReq]( ++ 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); ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ addReportReq->insert(uriToDbus); ++ }); ++ } ++ } ++ ++ static bool toDbusReportActions(crow::Response& res, ++ std::vector<std::string>& actions, ++ AddReportArgs& args) ++ { ++ size_t index = 0; ++ for (auto& action : actions) ++ { ++ if (action == "RedfishEvent") ++ { ++ args.emitsReadingsUpdate = true; ++ } ++ else if (action == "LogToMetricReportsCollection") ++ { ++ args.logToMetricReportsCollection = true; ++ } ++ else ++ { ++ messages::propertyValueNotInList( ++ res, action, "ReportActions/" + std::to_string(index)); ++ return false; ++ } ++ index++; ++ } ++ return true; ++ } ++ ++ static bool getUserParameters(crow::Response& res, const crow::Request& req, ++ AddReportArgs& args) ++ { ++ std::vector<nlohmann::json> metrics; ++ std::vector<std::string> reportActions; ++ std::optional<nlohmann::json> schedule; ++ if (!json_util::readJson(req, res, "Id", args.name, "Metrics", metrics, ++ "MetricReportDefinitionType", ++ args.reportingType, "ReportActions", ++ reportActions, "Schedule", schedule)) ++ { ++ return false; ++ } ++ ++ constexpr const char* allowedCharactersInName = ++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; ++ if (args.name.empty() || ++ args.name.find_first_not_of(allowedCharactersInName) != ++ std::string::npos) ++ { ++ BMCWEB_LOG_ERROR << "Failed to match " << args.name ++ << " with allowed character " ++ << allowedCharactersInName; ++ messages::propertyValueIncorrect(res, "Id", args.name); ++ return false; ++ } ++ ++ if (args.reportingType != "Periodic" && ++ args.reportingType != "OnRequest") ++ { ++ messages::propertyValueNotInList(res, args.reportingType, ++ "MetricReportDefinitionType"); ++ return false; ++ } ++ ++ if (!toDbusReportActions(res, reportActions, args)) ++ { ++ return false; ++ } ++ ++ if (args.reportingType == "Periodic") ++ { ++ if (!schedule) ++ { ++ messages::createFailedMissingReqProperties(res, "Schedule"); ++ return false; ++ } ++ ++ std::string durationStr; ++ if (!json_util::readJson(*schedule, res, "RecurrenceInterval", ++ durationStr)) ++ { ++ return false; ++ } ++ ++ std::optional<std::chrono::milliseconds> durationNum = ++ time_utils::fromDurationString(durationStr); ++ if (!durationNum) ++ { ++ messages::propertyValueIncorrect(res, "RecurrenceInterval", ++ durationStr); ++ return false; ++ } ++ args.interval = static_cast<uint64_t>(durationNum->count()); ++ } ++ ++ args.metrics.reserve(metrics.size()); ++ for (auto& m : metrics) ++ { ++ std::string id; ++ std::vector<std::string> uris; ++ if (!json_util::readJson(m, res, "MetricId", id, "MetricProperties", ++ uris)) ++ { ++ return false; ++ } ++ ++ args.metrics.emplace_back(std::move(id), std::move(uris)); ++ } ++ ++ return true; ++ } ++ ++ static bool getChassisSensorNode( ++ const std::shared_ptr<AsyncResp>& asyncResp, ++ const std::vector<std::pair<std::string, std::vector<std::string>>>& ++ metrics, ++ boost::container::flat_set<std::pair<std::string, std::string>>& ++ matched) ++ { ++ for (const auto& [id, uris] : metrics) ++ { ++ for (size_t i = 0; i < uris.size(); i++) ++ { ++ const std::string& uri = uris[i]; ++ std::string chassis; ++ std::string node; ++ ++ if (!boost::starts_with(uri, "/redfish/v1/Chassis/") || ++ !dbus::utility::getNthStringFromPath(uri, 3, chassis) || ++ !dbus::utility::getNthStringFromPath(uri, 4, node)) ++ { ++ BMCWEB_LOG_ERROR << "Failed to get chassis and sensor Node " ++ "from " ++ << uri; ++ messages::propertyValueIncorrect(asyncResp->res, uri, ++ "MetricProperties/" + ++ std::to_string(i)); ++ return false; ++ } ++ ++ if (boost::ends_with(node, "#")) ++ { ++ node.pop_back(); ++ } ++ ++ matched.emplace(std::move(chassis), std::move(node)); ++ } ++ } ++ return true; ++ } ++ ++ class AddReport ++ { ++ public: ++ AddReport(AddReportArgs argsIn, std::shared_ptr<AsyncResp> asyncResp) : ++ asyncResp{std::move(asyncResp)}, args{std::move(argsIn)} ++ {} ++ ~AddReport() ++ { ++ if (asyncResp->res.result() != boost::beast::http::status::ok) ++ { ++ return; ++ } ++ ++ telemetry::ReadingParameters readingParams; ++ readingParams.reserve(args.metrics.size()); ++ ++ for (const auto& [id, uris] : args.metrics) ++ { ++ std::vector<sdbusplus::message::object_path> dbusPaths; ++ dbusPaths.reserve(uris.size()); ++ ++ for (size_t i = 0; i < uris.size(); i++) ++ { ++ const std::string& uri = uris[i]; ++ auto el = uriToDbus.find(uri); ++ if (el == uriToDbus.end()) ++ { ++ BMCWEB_LOG_ERROR << "Failed to find DBus sensor " ++ "corresponding to URI " ++ << uri; ++ messages::propertyValueNotInList(asyncResp->res, uri, ++ "MetricProperties/" + ++ std::to_string(i)); ++ return; ++ } ++ ++ dbusPaths.emplace_back(el->second); ++ } ++ ++ nlohmann::json metadata; ++ metadata["MetricProperties"] = uris; ++ if (uris.size() == 1) ++ { ++ metadata["MetricProperty"] = uris[0]; ++ } ++ readingParams.emplace_back(std::move(dbusPaths), "SINGLE", id, ++ metadata.dump()); ++ } ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp = asyncResp, name = args.name]( ++ const boost::system::error_code ec, const std::string&) { ++ if (ec == boost::system::errc::file_exists) ++ { ++ messages::resourceAlreadyExists( ++ asyncResp->res, "MetricReportDefinition", "Id", ++ name); ++ return; ++ } ++ if (ec == boost::system::errc::too_many_files_open) ++ { ++ messages::createLimitReachedForResource(asyncResp->res); ++ return; ++ } ++ if (ec == boost::system::errc::argument_list_too_long) ++ { ++ messages::propertyValueNotInList( ++ asyncResp->res, "/Exceeds supported size/", ++ "Metrics"); ++ return; ++ } ++ if (ec == boost::system::errc::not_supported) ++ { ++ messages::propertyValueNotInList( ++ asyncResp->res, ++ "/Only single property per metric is supported/", ++ "MetricProperties"); ++ return; ++ } ++ if (ec == boost::system::errc::invalid_argument) ++ { ++ messages::propertyValueNotInList( ++ asyncResp->res, "/Less then MinInterval/", ++ "RecurrenceInterval"); ++ return; ++ } ++ if (ec) ++ { ++ messages::internalError(asyncResp->res); ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ return; ++ } ++ ++ messages::created(asyncResp->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); ++ } ++ ++ void insert( ++ const boost::container::flat_map<std::string, std::string>& el) ++ { ++ uriToDbus.insert(el.begin(), el.end()); ++ } ++ ++ private: ++ std::shared_ptr<AsyncResp> asyncResp; ++ AddReportArgs args; ++ boost::container::flat_map<std::string, std::string> uriToDbus{}; ++ }; + }; + + class MetricReportDefinition : public Node +@@ -73,9 +396,10 @@ class MetricReportDefinition : public Node + crow::connections::systemBus->async_method_call( + [asyncResp, + id](const boost::system::error_code ec, +- const std::vector<std::pair< +- std::string, std::variant<bool, ReadingParameters, +- std::string, uint64_t>>>& ret) { ++ const std::vector< ++ std::pair<std::string, ++ std::variant<bool, telemetry::ReadingParameters, ++ std::string, uint64_t>>>& ret) { + if (ec.value() == EBADR) + { + messages::resourceNotFound(asyncResp->res, schemaType, id); +@@ -95,15 +419,11 @@ class MetricReportDefinition : public Node + telemetry::reportInterface); + } + +- using ReadingParameters = +- std::vector<std::tuple<std::vector<sdbusplus::message::object_path>, +- std::string, std::string, std::string>>; +- + static void fillReportDefinition( + const std::shared_ptr<AsyncResp>& asyncResp, const std::string& id, +- const std::vector< +- std::pair<std::string, std::variant<bool, ReadingParameters, +- std::string, uint64_t>>>& ret) ++ const std::vector<std::pair< ++ std::string, std::variant<bool, telemetry::ReadingParameters, ++ std::string, uint64_t>>>& ret) + { + asyncResp->res.jsonValue["@odata.type"] = schemaType; + asyncResp->res.jsonValue["@odata.id"] = +@@ -117,7 +437,7 @@ class MetricReportDefinition : public Node + + const bool* emitsReadingsUpdate = nullptr; + const bool* logToMetricReportsCollection = nullptr; +- const ReadingParameters* readingParams = nullptr; ++ const telemetry::ReadingParameters* readingParams = nullptr; + const std::string* reportingType = nullptr; + const uint64_t* interval = nullptr; + for (const auto& [key, var] : ret) +@@ -132,7 +452,7 @@ class MetricReportDefinition : public Node + } + else if (key == "ReadingParameters") + { +- readingParams = std::get_if<ReadingParameters>(&var); ++ readingParams = std::get_if<telemetry::ReadingParameters>(&var); + } + else if (key == "ReportingType") + { +@@ -180,6 +500,44 @@ class MetricReportDefinition : public Node + time_utils::toDurationString(std::chrono::milliseconds(*interval)); + } + ++ void doDelete(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>& params) override ++ { ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ if (params.size() != 1) ++ { ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ const std::string& id = params[0]; ++ const std::string reportPath = telemetry::getDbusReportPath(id); ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, id](const boost::system::error_code ec) { ++ /* ++ * boost::system::errc and std::errc are missing value for ++ * EBADR error that is defined in Linux. ++ */ ++ if (ec.value() == EBADR) ++ { ++ messages::resourceNotFound(asyncResp->res, schemaType, id); ++ return; ++ } ++ ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ asyncResp->res.result(boost::beast::http::status::no_content); ++ }, ++ telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete", ++ "Delete"); ++ } ++ + static constexpr const char* schemaType = + "#MetricReportDefinition.v1_3_0.MetricReportDefinition"; + }; +-- +2.16.6 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch new file mode 100644 index 000000000..d81d654f1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch @@ -0,0 +1,709 @@ +From b074a84560349fdbd46604ab0b8c75804de09fef Mon Sep 17 00:00:00 2001 +From: "Wludzik, Jozef" <jozef.wludzik@intel.com> +Date: Mon, 8 Jun 2020 17:15:54 +0200 +Subject: [PATCH 3/4] Add support for MetricDefinition scheme + +Added MetricDefinition node to redfish core. Now user is able to +get all possible metrics that are present in system and are +supported by TelemetryService. +Added generic function to fill ReadingUnits and ReadingType +in Sensor scheme. + +Tested: + - Succesfully passed RedfishServiceValidator.py + - Validated a presence of MetricDefinition members + +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 + + redfish-core/include/utils/telemetry_utils.hpp | 56 ++--- + redfish-core/lib/metric_definition.hpp | 269 +++++++++++++++++++++++++ + redfish-core/lib/metric_report_definition.hpp | 22 ++ + redfish-core/lib/power.hpp | 4 +- + redfish-core/lib/sensors.hpp | 104 +++++++--- + redfish-core/lib/telemetry_service.hpp | 2 + + redfish-core/lib/thermal.hpp | 4 +- + 8 files changed, 410 insertions(+), 54 deletions(-) + create mode 100644 redfish-core/lib/metric_definition.hpp + +diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp +index 2587b37..705f490 100644 +--- a/redfish-core/include/redfish.hpp ++++ b/redfish-core/include/redfish.hpp +@@ -25,6 +25,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" +@@ -211,6 +212,8 @@ class RedfishService + nodes.emplace_back(std::make_unique<HypervisorSystem>(app)); + + nodes.emplace_back(std::make_unique<TelemetryService>(app)); ++ nodes.emplace_back(std::make_unique<MetricDefinitionCollection>(app)); ++ nodes.emplace_back(std::make_unique<MetricDefinition>(app)); + nodes.emplace_back( + std::make_unique<MetricReportDefinitionCollection>(app)); + nodes.emplace_back(std::make_unique<MetricReportDefinition>(app)); +diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp +index acb739d..c13a79b 100644 +--- a/redfish-core/include/utils/telemetry_utils.hpp ++++ b/redfish-core/include/utils/telemetry_utils.hpp +@@ -8,6 +8,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 = +@@ -15,6 +17,36 @@ constexpr const char* metricReportUri = + constexpr const char* reportDir = + "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/"; + ++inline void dbusPathsToMembers(const std::shared_ptr<AsyncResp>& asyncResp, ++ const std::vector<std::string>& paths, ++ const std::string& uri) ++{ ++ nlohmann::json& members = asyncResp->res.jsonValue["Members"]; ++ members = nlohmann::json::array(); ++ ++ for (const std::string& path : paths) ++ { ++ std::size_t pos = path.rfind('/'); ++ if (pos == std::string::npos) ++ { ++ BMCWEB_LOG_ERROR << "Failed to find '/' in " << path; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ if (path.size() <= (pos + 1)) ++ { ++ BMCWEB_LOG_ERROR << "Failed to parse path " << path; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ members.push_back({{"@odata.id", uri + path.substr(pos + 1)}}); ++ } ++ ++ asyncResp->res.jsonValue["Members@odata.count"] = members.size(); ++} ++ + inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp, + const std::string& uri) + { +@@ -30,29 +62,7 @@ inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp, + return; + } + +- nlohmann::json& members = asyncResp->res.jsonValue["Members"]; +- members = nlohmann::json::array(); +- +- for (const std::string& path : reportPaths) +- { +- std::size_t pos = path.rfind('/'); +- if (pos == std::string::npos) +- { +- BMCWEB_LOG_ERROR << "Failed to find '/' in " << path; +- messages::internalError(asyncResp->res); +- return; +- } +- if (path.size() <= (pos + 1)) +- { +- BMCWEB_LOG_ERROR << "Failed to parse path " << path; +- messages::internalError(asyncResp->res); +- return; +- } +- +- members.push_back({{"@odata.id", uri + path.substr(pos + 1)}}); +- } +- +- asyncResp->res.jsonValue["Members@odata.count"] = members.size(); ++ dbusPathsToMembers(asyncResp, reportPaths, uri); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", +diff --git a/redfish-core/lib/metric_definition.hpp b/redfish-core/lib/metric_definition.hpp +new file mode 100644 +index 0000000..f037ed2 +--- /dev/null ++++ b/redfish-core/lib/metric_definition.hpp +@@ -0,0 +1,269 @@ ++#pragma once ++ ++#include "node.hpp" ++#include "sensors.hpp" ++#include "utils/telemetry_utils.hpp" ++ ++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, ++ std::vector<std::string>& chassisList) { ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG << "DBus call error: " << ec.value(); ++ return; ++ } ++ ++ std::vector<std::string> chassisNames; ++ chassisNames.reserve(chassisList.size()); ++ for (const std::string& chassisPath : chassisList) ++ { ++ size_t pos = chassisPath.rfind('/'); ++ if (pos == std::string::npos) ++ { ++ continue; ++ } ++ chassisNames.push_back(chassisPath.substr(pos + 1)); ++ } ++ ++ callback(chassisNames); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", ++ "/xyz/openbmc_project/inventory", 0, interfaces); ++} ++} // namespace utils ++ ++class MetricDefinitionCollection : public Node ++{ ++ public: ++ MetricDefinitionCollection(App& app) : ++ Node(app, "/redfish/v1/TelemetryService/MetricDefinitions/") ++ { ++ entityPrivileges = { ++ {boost::beast::http::verb::get, {{"Login"}}}, ++ {boost::beast::http::verb::head, {{"Login"}}}, ++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::put, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; ++ } ++ ++ private: ++ void doGet(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>&) override ++ { ++ res.jsonValue["@odata.type"] = "#MetricDefinitionCollection." ++ "MetricDefinitionCollection"; ++ res.jsonValue["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricDefinitions"; ++ res.jsonValue["Name"] = "Metric Definition Collection"; ++ res.jsonValue["Members"] = nlohmann::json::array(); ++ res.jsonValue["Members@odata.count"] = 0; ++ ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ auto collectionReduce = std::make_shared<CollectionGather>(asyncResp); ++ utils::getChassisNames( ++ [asyncResp, ++ collectionReduce](const std::vector<std::string>& chassisNames) { ++ for (const std::string& chassisName : chassisNames) ++ { ++ for (const auto& [sensorNode, _] : sensors::dbus::paths) ++ { ++ BMCWEB_LOG_INFO << "Chassis: " << chassisName ++ << " sensor: " << sensorNode; ++ retrieveUriToDbusMap( ++ chassisName, sensorNode.data(), ++ [asyncResp, collectionReduce]( ++ 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); ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ collectionReduce->insert(uriToDbus); ++ }); ++ } ++ } ++ }); ++ } ++ ++ class CollectionGather ++ { ++ public: ++ CollectionGather(const std::shared_ptr<AsyncResp>& asyncResp) : ++ asyncResp{asyncResp} ++ {} ++ ++ ~CollectionGather() ++ { ++ if (asyncResp->res.result() != boost::beast::http::status::ok) ++ { ++ return; ++ } ++ ++ telemetry::dbusPathsToMembers( ++ asyncResp, ++ std::vector<std::string>(dbusTypes.begin(), dbusTypes.end()), ++ telemetry::metricDefinitionUri); ++ } ++ ++ void insert( ++ const boost::container::flat_map<std::string, std::string>& el) ++ { ++ for (const auto& [_, dbusSensor] : el) ++ { ++ size_t pos = dbusSensor.rfind('/'); ++ if (pos == std::string::npos) ++ { ++ BMCWEB_LOG_ERROR << "Received invalid DBus Sensor Path = " ++ << dbusSensor; ++ continue; ++ } ++ ++ dbusTypes.insert(dbusSensor.substr(0, pos)); ++ } ++ } ++ ++ private: ++ const std::shared_ptr<AsyncResp> asyncResp; ++ boost::container::flat_set<std::string> dbusTypes; ++ }; ++}; ++ ++class MetricDefinition : public Node ++{ ++ public: ++ MetricDefinition(App& app) : ++ Node(app, "/redfish/v1/TelemetryService/MetricDefinitions/<str>/", ++ std::string()) ++ { ++ entityPrivileges = { ++ {boost::beast::http::verb::get, {{"Login"}}}, ++ {boost::beast::http::verb::head, {{"Login"}}}, ++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::put, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, ++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; ++ } ++ ++ private: ++ void doGet(crow::Response& res, const crow::Request&, ++ const std::vector<std::string>& params) override ++ { ++ auto asyncResp = std::make_shared<AsyncResp>(res); ++ if (params.size() != 1) ++ { ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ const std::string& id = params[0]; ++ auto definitionGather = ++ std::make_shared<DefinitionGather>(asyncResp, id); ++ utils::getChassisNames( ++ [asyncResp, ++ definitionGather](const std::vector<std::string>& chassisNames) { ++ for (const std::string& chassisName : chassisNames) ++ { ++ for (const auto& [sensorNode, dbusPaths] : ++ sensors::dbus::paths) ++ { ++ retrieveUriToDbusMap( ++ chassisName, sensorNode.data(), ++ [asyncResp, definitionGather]( ++ 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); ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ definitionGather->insert(uriToDbus); ++ }); ++ } ++ } ++ }); ++ } ++ ++ class DefinitionGather ++ { ++ public: ++ DefinitionGather(const std::shared_ptr<AsyncResp>& asyncResp, ++ const std::string& id) : ++ id(id), ++ pattern{'/' + id + '/'}, asyncResp{asyncResp} ++ {} ++ ~DefinitionGather() ++ { ++ if (asyncResp->res.result() != boost::beast::http::status::ok) ++ { ++ return; ++ } ++ if (redfishSensors.empty()) ++ { ++ messages::resourceNotFound(asyncResp->res, schemaType, id); ++ return; ++ } ++ ++ asyncResp->res.jsonValue["MetricProperties"] = redfishSensors; ++ asyncResp->res.jsonValue["Id"] = id; ++ asyncResp->res.jsonValue["Name"] = id; ++ asyncResp->res.jsonValue["@odata.id"] = ++ telemetry::metricDefinitionUri + id; ++ asyncResp->res.jsonValue["@odata.type"] = schemaType; ++ asyncResp->res.jsonValue["MetricDataType"] = "Decimal"; ++ asyncResp->res.jsonValue["MetricType"] = "Numeric"; ++ asyncResp->res.jsonValue["IsLinear"] = true; ++ asyncResp->res.jsonValue["Units"] = sensors::toReadingUnits(id); ++ } ++ ++ void insert( ++ const boost::container::flat_map<std::string, std::string>& el) ++ { ++ for (const auto& [redfishSensor, dbusSensor] : el) ++ { ++ if (dbusSensor.find(pattern) != std::string::npos) ++ { ++ redfishSensors.push_back(redfishSensor); ++ } ++ } ++ } ++ ++ const std::string id; ++ const std::string pattern; ++ ++ private: ++ const std::shared_ptr<AsyncResp> asyncResp; ++ std::vector<std::string> redfishSensors; ++ }; ++ ++ static constexpr const char* schemaType = ++ "#MetricDefinition.v1_0_3.MetricDefinition"; ++}; ++ ++} // namespace redfish +diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp +index d5a540d..03f0b82 100644 +--- a/redfish-core/lib/metric_report_definition.hpp ++++ b/redfish-core/lib/metric_report_definition.hpp +@@ -269,6 +269,8 @@ class MetricReportDefinitionCollection : public Node + { + std::vector<sdbusplus::message::object_path> dbusPaths; + dbusPaths.reserve(uris.size()); ++ std::string sensorType; ++ bool invalidType = false; + + for (size_t i = 0; i < uris.size(); i++) + { +@@ -286,6 +288,21 @@ class MetricReportDefinitionCollection : public Node + } + + dbusPaths.emplace_back(el->second); ++ ++ if (invalidType) ++ { ++ continue; ++ } ++ std::string tmp; ++ dbus::utility::getNthStringFromPath(el->second, 3, tmp); ++ if (sensorType.empty()) ++ { ++ sensorType = std::move(tmp); ++ } ++ else if (sensorType != tmp) ++ { ++ invalidType = true; ++ } + } + + nlohmann::json metadata; +@@ -294,6 +311,11 @@ class MetricReportDefinitionCollection : public Node + { + metadata["MetricProperty"] = uris[0]; + } ++ if (!sensorType.empty() && !invalidType) ++ { ++ metadata["MetricDefinition"]["@odata.id"] = ++ telemetry::metricDefinitionUri + sensorType; ++ } + readingParams.emplace_back(std::move(dbusPaths), "SINGLE", id, + metadata.dump()); + } +diff --git a/redfish-core/lib/power.hpp b/redfish-core/lib/power.hpp +index 1c7a009..99c45ef 100644 +--- a/redfish-core/lib/power.hpp ++++ b/redfish-core/lib/power.hpp +@@ -153,7 +153,7 @@ class Power : public Node + res.jsonValue["PowerControl"] = nlohmann::json::array(); + + auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( +- res, chassisName, sensors::dbus::types.at(sensors::node::power), ++ res, chassisName, sensors::dbus::paths.at(sensors::node::power), + sensors::node::power); + + getChassisData(sensorAsyncResp); +@@ -336,7 +336,7 @@ class Power : public Node + + const std::string& chassisName = params[0]; + auto asyncResp = std::make_shared<SensorsAsyncResp>( +- res, chassisName, sensors::dbus::types.at(sensors::node::power), ++ res, chassisName, sensors::dbus::paths.at(sensors::node::power), + sensors::node::power); + + std::optional<std::vector<nlohmann::json>> voltageCollections; +diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp +index 567cb0c..363713d 100644 +--- a/redfish-core/lib/sensors.hpp ++++ b/redfish-core/lib/sensors.hpp +@@ -54,9 +54,10 @@ static constexpr std::string_view thermal = "Thermal"; + + namespace dbus + { ++ + static const boost::container::flat_map<std::string_view, + std::vector<const char*>> +- types = {{node::power, ++ paths = {{node::power, + {"/xyz/openbmc_project/sensors/voltage", + "/xyz/openbmc_project/sensors/power"}}, + {node::sensors, +@@ -67,6 +68,64 @@ static const boost::container::flat_map<std::string_view, + {"/xyz/openbmc_project/sensors/fan_tach", + "/xyz/openbmc_project/sensors/temperature", + "/xyz/openbmc_project/sensors/fan_pwm"}}}; ++} // namespace dbus ++ ++inline const char* toReadingType(const std::string& sensorType) ++{ ++ if (sensorType == "voltage") ++ { ++ return "Voltage"; ++ } ++ if (sensorType == "power") ++ { ++ return "Power"; ++ } ++ if (sensorType == "current") ++ { ++ return "Current"; ++ } ++ if (sensorType == "fan_tach") ++ { ++ return "Rotational"; ++ } ++ if (sensorType == "temperature") ++ { ++ return "Temperature"; ++ } ++ if (sensorType == "fan_pwm" || sensorType == "utilization") ++ { ++ return "Percent"; ++ } ++ return ""; ++} ++ ++inline const char* toReadingUnits(const std::string& sensorType) ++{ ++ if (sensorType == "voltage") ++ { ++ return "V"; ++ } ++ if (sensorType == "power") ++ { ++ return "W"; ++ } ++ if (sensorType == "current") ++ { ++ return "A"; ++ } ++ if (sensorType == "fan_tach") ++ { ++ return "RPM"; ++ } ++ if (sensorType == "temperature") ++ { ++ return "Cel"; ++ } ++ if (sensorType == "fan_pwm" || sensorType == "utilization") ++ { ++ return "%"; ++ } ++ return ""; + } + } // namespace sensors + +@@ -90,19 +149,20 @@ class SensorsAsyncResp + }; + + SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn, +- const std::vector<const char*>& typesIn, ++ const std::vector<const char*>& matchPathsIn, + const std::string_view& subNode) : + res(response), +- chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode) ++ chassisId(chassisIdIn), matchPaths(matchPathsIn), ++ chassisSubNode(subNode) + {} + + // Store extra data about sensor mapping and return it in callback + SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn, +- const std::vector<const char*>& typesIn, ++ const std::vector<const char*>& matchPathsIn, + const std::string_view& subNode, + DataCompleteCb&& creationComplete) : + res(response), +- chassisId(chassisIdIn), types(typesIn), ++ chassisId(chassisIdIn), matchPaths(matchPathsIn), + chassisSubNode(subNode), metadata{std::vector<SensorData>()}, + dataComplete{std::move(creationComplete)} + {} +@@ -161,7 +221,7 @@ class SensorsAsyncResp + + crow::Response& res; + const std::string chassisId; +- const std::vector<const char*> types; ++ const std::vector<const char*> matchPaths; + const std::string chassisSubNode; + + private: +@@ -320,20 +380,20 @@ void getConnections( + * made, and eliminate Power sensors when a Thermal request is made. + */ + inline void reduceSensorList( +- const std::shared_ptr<SensorsAsyncResp>& SensorsAsyncResp, ++ const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, + const std::vector<std::string>* allSensors, + const std::shared_ptr<boost::container::flat_set<std::string>>& + activeSensors) + { +- if (SensorsAsyncResp == nullptr) ++ if (sensorsAsyncResp == nullptr) + { + return; + } + if ((allSensors == nullptr) || (activeSensors == nullptr)) + { + messages::resourceNotFound( +- SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode, +- SensorsAsyncResp->chassisSubNode == sensors::node::thermal ++ sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode, ++ sensorsAsyncResp->chassisSubNode == sensors::node::thermal + ? "Temperatures" + : "Voltages"); + +@@ -345,11 +405,11 @@ inline void reduceSensorList( + return; + } + +- for (const char* type : SensorsAsyncResp->types) ++ for (const char* path : sensorsAsyncResp->matchPaths) + { + for (const std::string& sensor : *allSensors) + { +- if (boost::starts_with(sensor, type)) ++ if (boost::starts_with(sensor, path)) + { + activeSensors->emplace(sensor); + } +@@ -853,18 +913,8 @@ inline void objectInterfacesToJson( + if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors) + { + sensor_json["@odata.type"] = "#Sensor.v1_0_0.Sensor"; +- if (sensorType == "power") +- { +- sensor_json["ReadingUnits"] = "Watts"; +- } +- else if (sensorType == "current") +- { +- sensor_json["ReadingUnits"] = "Amperes"; +- } +- else if (sensorType == "utilization") +- { +- sensor_json["ReadingUnits"] = "Percent"; +- } ++ sensor_json["ReadingType"] = sensors::toReadingType(sensorType); ++ sensor_json["ReadingUnits"] = sensors::toReadingUnits(sensorType); + } + else if (sensorType == "temperature") + { +@@ -2976,8 +3026,8 @@ inline void retrieveUriToDbusMap(const std::string& chassis, + const std::string& node, + SensorsAsyncResp::DataCompleteCb&& mapComplete) + { +- auto typesIt = sensors::dbus::types.find(node); +- if (typesIt == sensors::dbus::types.end()) ++ auto typesIt = sensors::dbus::paths.find(node); ++ if (typesIt == sensors::dbus::paths.end()) + { + BMCWEB_LOG_ERROR << "Wrong node provided : " << node; + mapComplete(boost::beast::http::status::bad_request, {}); +@@ -3027,7 +3077,7 @@ class SensorCollection : public Node + const std::string& chassisId = params[0]; + std::shared_ptr<SensorsAsyncResp> asyncResp = + std::make_shared<SensorsAsyncResp>( +- res, chassisId, sensors::dbus::types.at(sensors::node::sensors), ++ res, chassisId, sensors::dbus::paths.at(sensors::node::sensors), + sensors::node::sensors); + + auto getChassisCb = +diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp +index a6acc34..8105d86 100644 +--- a/redfish-core/lib/telemetry_service.hpp ++++ b/redfish-core/lib/telemetry_service.hpp +@@ -34,6 +34,8 @@ class TelemetryService : public Node + + res.jsonValue["LogService"]["@odata.id"] = + "/redfish/v1/Managers/bmc/LogServices/Journal"; ++ res.jsonValue["MetricDefinitions"]["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricDefinitions"; + res.jsonValue["MetricReportDefinitions"]["@odata.id"] = + "/redfish/v1/TelemetryService/MetricReportDefinitions"; + res.jsonValue["MetricReports"]["@odata.id"] = +diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp +index 8e01bee..00acdf9 100644 +--- a/redfish-core/lib/thermal.hpp ++++ b/redfish-core/lib/thermal.hpp +@@ -48,7 +48,7 @@ class Thermal : public Node + } + const std::string& chassisName = params[0]; + auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( +- res, chassisName, sensors::dbus::types.at(sensors::node::thermal), ++ res, chassisName, sensors::dbus::paths.at(sensors::node::thermal), + sensors::node::thermal); + + // TODO Need to get Chassis Redundancy information. +@@ -71,7 +71,7 @@ class Thermal : public Node + allCollections; + + auto asyncResp = std::make_shared<SensorsAsyncResp>( +- res, chassisName, sensors::dbus::types.at(sensors::node::thermal), ++ res, chassisName, sensors::dbus::paths.at(sensors::node::thermal), + sensors::node::thermal); + + if (!json_util::readJson(req, asyncResp->res, "Temperatures", +-- +2.16.6 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch new file mode 100644 index 000000000..08dcb385d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch @@ -0,0 +1,330 @@ +From 5b775e33221638a34c4aad0e2edeffc447d50fab Mon Sep 17 00:00:00 2001 +From: "Wludzik, Jozef" <jozef.wludzik@intel.com> +Date: Fri, 4 Dec 2020 14:48:41 +0100 +Subject: [PATCH 4/4] Sync Telmetry service with EventService + +Now assembling MetricReport is done properly and is +covered in one place - MetricReport node. +Updated method of fetching Readings from Telemetry by +EventService. Using ReportUpdate signal is no longer +supported. + +Tested: + - Received MetricReport in EventListener server after + adding subscription to EventService. + +Change-Id: I2fc1841a6c9259a8bff30b34bddc0d4aabd41912 +Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com> +--- + redfish-core/include/event_service_manager.hpp | 156 +++++++++---------------- + redfish-core/lib/metric_report.hpp | 35 +++--- + 2 files changed, 74 insertions(+), 117 deletions(-) + +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 54dafb4..1cdb9a6 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 "node.hpp" + #include "registries.hpp" + #include "registries/base_message_registry.hpp" +@@ -510,48 +511,29 @@ class Subscription + } + #endif + +- void filterAndSendReports(const std::string& id2, +- const std::string& readingsTs, +- const ReadingsObjType& readings) ++ void filterAndSendReports( ++ const std::string& id, ++ const std::variant<MetricReport::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 json; ++ if (!MetricReport::fillReport(json, 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)}}; ++ 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()); ++ this->sendEvent(json.dump()); + } + + void updateRetryConfig(const uint32_t retryAttempts, +@@ -1342,56 +1324,71 @@ class EventServiceManager + } + + #endif +- +- void getMetricReading(const std::string& service, +- const std::string& objPath, const std::string& intf) ++ void unregisterMetricReportSignal() + { +- std::size_t found = objPath.find_last_of('/'); +- if (found == std::string::npos) ++ if (matchTelemetryMonitor) + { +- BMCWEB_LOG_DEBUG << "Invalid objPath received"; +- return; ++ BMCWEB_LOG_DEBUG << "Metrics report signal - Unregister"; ++ matchTelemetryMonitor.reset(); ++ matchTelemetryMonitor = nullptr; + } ++ } + +- std::string idStr = objPath.substr(found + 1); +- if (idStr.empty()) ++ void registerMetricReportSignal() ++ { ++ if (!serviceEnabled || matchTelemetryMonitor) + { +- BMCWEB_LOG_DEBUG << "Invalid ID in objPath"; ++ BMCWEB_LOG_DEBUG << "Not registering metric report signal."; + 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 << "Metrics report signal - Register"; ++ 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, ++ [this](sdbusplus::message::message& msg) { ++ if (msg.is_method_error()) + { +- BMCWEB_LOG_DEBUG +- << "D-Bus call failed to GetAll metric readings."; ++ BMCWEB_LOG_ERROR << "TelemetryMonitor Signal error"; + return; + } + +- const int32_t* timestampPtr = +- std::get_if<int32_t>(&resp["Timestamp"]); +- if (!timestampPtr) ++ std::string intf; ++ std::vector<std::pair< ++ std::string, std::variant<MetricReport::TimestampReadings>>> ++ props; ++ std::vector<std::string> invalidProp; ++ ++ msg.read(intf, props, invalidProp); ++ if (intf != "xyz.openbmc_project.Telemetry.Report") + { +- BMCWEB_LOG_DEBUG << "Failed to Get timestamp."; + return; + } + +- ReadingsObjType* readingsPtr = +- std::get_if<ReadingsObjType>(&resp["Readings"]); +- if (!readingsPtr) ++ const std::variant<MetricReport::TimestampReadings>* varPtr = ++ nullptr; ++ for (const auto& [key, var] : props) ++ { ++ if (key == "Readings") ++ { ++ varPtr = &var; ++ break; ++ } ++ } ++ if (!varPtr) + { +- BMCWEB_LOG_DEBUG << "Failed to Get Readings property."; + return; + } + +- if (!readingsPtr->size()) ++ std::string id; ++ if (!dbus::utility::getNthStringFromPath(msg.get_path(), 5, id)) + { +- BMCWEB_LOG_DEBUG << "No metrics report to be transferred"; ++ BMCWEB_LOG_ERROR << "Failed to get Id from path"; + return; + } + +@@ -1401,52 +1398,9 @@ class EventServiceManager + std::shared_ptr<Subscription> entry = it.second; + if (entry->eventFormatType == metricReportFormatType) + { +- entry->filterAndSendReports( +- idStr, crow::utility::getDateTime(*timestampPtr), +- *readingsPtr); ++ entry->filterAndSendReports(id, *varPtr); + } + } +- }, +- service, objPath, "org.freedesktop.DBus.Properties", "GetAll", +- intf); +- } +- +- void unregisterMetricReportSignal() +- { +- if (matchTelemetryMonitor) +- { +- BMCWEB_LOG_DEBUG << "Metrics report signal - Unregister"; +- matchTelemetryMonitor.reset(); +- matchTelemetryMonitor = nullptr; +- } +- } +- +- void registerMetricReportSignal() +- { +- if (!serviceEnabled || matchTelemetryMonitor) +- { +- BMCWEB_LOG_DEBUG << "Not registering metric report signal."; +- return; +- } +- +- BMCWEB_LOG_DEBUG << "Metrics report signal - Register"; +- std::string matchStr( +- "type='signal',member='ReportUpdate', " +- "interface='xyz.openbmc_project.MonitoringService.Report'"); +- +- matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match::match>( +- *crow::connections::systemBus, matchStr, +- [this](sdbusplus::message::message& msg) { +- if (msg.is_method_error()) +- { +- BMCWEB_LOG_ERROR << "TelemetryMonitor Signal error"; +- return; +- } +- +- std::string service = msg.get_sender(); +- std::string objPath = msg.get_path(); +- std::string intf = msg.get_interface(); +- getMetricReading(service, objPath, intf); + }); + } + +diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp +index 050304c..c2013cc 100644 +--- a/redfish-core/lib/metric_report.hpp ++++ b/redfish-core/lib/metric_report.hpp +@@ -52,6 +52,10 @@ class MetricReport : public Node + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; + } + ++ using Readings = ++ std::vector<std::tuple<std::string, std::string, double, uint64_t>>; ++ using TimestampReadings = std::tuple<uint64_t, Readings>; ++ + private: + void doGet(crow::Response& res, const crow::Request&, + const std::vector<std::string>& params) override +@@ -92,7 +96,10 @@ class MetricReport : public Node + return; + } + +- fillReport(asyncResp, id, ret); ++ if (!fillReport(asyncResp->res.jsonValue, id, ret)) ++ { ++ messages::internalError(asyncResp->res); ++ } + }, + telemetry::service, reportPath, + "org.freedesktop.DBus.Properties", "Get", +@@ -102,10 +109,6 @@ class MetricReport : public Node + "Update"); + } + +- using Readings = +- std::vector<std::tuple<std::string, std::string, double, uint64_t>>; +- using TimestampReadings = std::tuple<uint64_t, Readings>; +- + static nlohmann::json toMetricValues(const Readings& readings) + { + nlohmann::json metricValues = nlohmann::json::array_t(); +@@ -130,15 +133,15 @@ class MetricReport : public Node + return metricValues; + } + +- static void fillReport(const std::shared_ptr<AsyncResp>& asyncResp, +- const std::string& id, ++ public: ++ static bool fillReport(nlohmann::json& json, const std::string& id, + const std::variant<TimestampReadings>& var) + { +- asyncResp->res.jsonValue["@odata.type"] = schemaType; +- 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"] = schemaType; ++ json["@odata.id"] = telemetry::metricReportUri + id; ++ json["Id"] = id; ++ json["Name"] = id; ++ json["MetricReportDefinition"]["@odata.id"] = + telemetry::metricReportDefinitionUri + id; + + const TimestampReadings* timestampReadings = +@@ -146,14 +149,14 @@ class MetricReport : public Node + 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; + } + + static constexpr const char* schemaType = +-- +2.16.6 + 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..833fabfec --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README @@ -0,0 +1,16 @@ +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: +- Redfish TelemetryService schema implementation + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/31692/54 + +- Add POST and DELETE in MetricReportDefinitions + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32536/46 + +- Add support for MetricDefinition scheme + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/42 + +- Sync Telmetry service with EventService + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/38798/9 + 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..ac095ba61 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend @@ -0,0 +1,53 @@ +SRC_URI = "git://github.com/openbmc/bmcweb.git" +SRCREV = "f16f62633a64f386fd0382703ff0949ea177f457" + +DEPENDS += "boost-url" +RDEPENDS_${PN} += "phosphor-nslcd-authority-cert-config" + +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://0004-bmcweb-handle-device-or-resource-busy-exception.patch \ + file://0005-EventService-https-client-support.patch \ + file://0006-Define-Redfish-interface-Registries-Bios.patch \ + file://0007-BIOS-config-Add-support-for-PATCH-operation.patch \ + file://0008-Add-support-to-ResetBios-action.patch \ + file://0009-Add-support-to-ChangePassword-action.patch \ + file://0010-managers-add-attributes-for-Manager.CommandShell.patch \ + file://0034-recommended-fixes-by-crypto-review-team.patch \ +" + + +# Temporary downstream mirror of upstream patches, see telemetry\README for details +SRC_URI += "file://telemetry/0001-Redfish-TelemetryService-schema-implementation.patch \ + file://telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch \ + file://telemetry/0003-Add-support-for-MetricDefinition-scheme.patch \ + file://telemetry/0004-Sync-Telmetry-service-with-EventService.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" + +# Enable Validation unsecure based on IMAGE_FEATURES +EXTRA_OEMESON += "${@bb.utils.contains('EXTRA_IMAGE_FEATURES', 'validation-unsecure', '-Dvalidate-unsecure-feature=enabled', '', d)}" + 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..616fb9a75 --- /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..656207de6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json @@ -0,0 +1,178 @@ +{ + "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, + "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..2d892ad1a --- /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..ccf326a91 --- /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 c1925ab965271875d4e9333be85cdfe8e1516948 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 dcfda11..780afbc 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -784,8 +784,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"; + +@@ -794,7 +792,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..7a7fd0859 --- /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 ad93a6e17310d92ef07b8d367b23c93793562d0f 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 cmdtool.efi can work +correctly through KCS channel. +--- + host-cmd-manager.cpp | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/host-cmd-manager.cpp b/host-cmd-manager.cpp +index f3aba7f..465eb81 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.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0056-add-SetInProgress-to-get-set-boot-option-cmd.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0056-add-SetInProgress-to-get-set-boot-option-cmd.patch new file mode 100644 index 000000000..862e9baa8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0056-add-SetInProgress-to-get-set-boot-option-cmd.patch @@ -0,0 +1,102 @@ +From d5cfc5a0aaa50cc94054886e5cb7be25ef167c71 Mon Sep 17 00:00:00 2001 +From: huanghe <he.huang@intel.com> +Date: Sat, 10 Oct 2020 14:40:00 +0800 +Subject: [PATCH 7/7] Add set in progress paramter to set/get boot option + command + +Signed-off-by: huanghe <he.huang@intel.com> +--- + chassishandler.cpp | 34 ++++++++++++++++++++++++++++++++++ + chassishandler.hpp | 3 +++ + 2 files changed, 37 insertions(+) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index f043340..322aa9e 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -1564,6 +1564,10 @@ static ipmi::Cc setBootMode(const Mode::Modes& mode) + return ipmi::ccSuccess; + } + ++static constexpr uint8_t setComplete = 0x0; ++static constexpr uint8_t setInProgress = 0x1; ++static uint8_t transferStatus = setComplete; ++ + /** @brief implements the Get Chassis system boot option + * @param bootOptionParameter - boot option parameter selector + * @param reserved1 - reserved bit +@@ -1598,6 +1602,14 @@ ipmi::RspType<ipmi::message::Payload> + + IpmiValue bootOption = ipmiDefault; + ++ ++ if (static_cast<uint8_t>(bootOptionParameter) == ++ static_cast<uint8_t>(BootOptionParameter::setInProgress)) ++ { ++ response.pack(bootOptionParameter,reserved1,transferStatus); ++ return ipmi::responseSuccess(std::move(response)); ++ } ++ + /* + * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. + * This is the only parameter used by petitboot. +@@ -1719,6 +1731,28 @@ ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx, + using namespace boot_options; + ipmi::Cc rc; + ++ if (parameterSelector == ++ static_cast<uint7_t>(BootOptionParameter::setInProgress)) ++ { ++ uint2_t setInProgressFlag; ++ uint6_t rsvd; ++ if (data.unpack(setInProgressFlag,rsvd) != 0 || ++ !data.fullyUnpacked()) ++ { ++ return ipmi::responseReqDataLenInvalid(); ++ } ++ if (rsvd) ++ { ++ return ipmi::responseInvalidFieldRequest(); ++ } ++ if ((transferStatus == setInProgress) && ++ ((uint8_t)setInProgressFlag != setComplete)) ++ { ++ return ipmi::response(IPMI_CC_FAIL_SET_IN_PROGRESS); ++ } ++ transferStatus = (uint8_t)setInProgressFlag; ++ } ++ + /* 000101 + * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. + * This is the only parameter used by petitboot. +diff --git a/chassishandler.hpp b/chassishandler.hpp +index 93de2c0..5976abc 100644 +--- a/chassishandler.hpp ++++ b/chassishandler.hpp +@@ -25,6 +25,7 @@ enum ipmi_chassis_return_codes + { + IPMI_OK = 0x0, + IPMI_CC_PARM_NOT_SUPPORTED = 0x80, ++ IPMI_CC_FAIL_SET_IN_PROGRESS = 0x81, + }; + + // Generic completion codes, +@@ -46,6 +47,7 @@ enum ipmi_chassis_control_cmds : uint8_t + }; + enum class BootOptionParameter : size_t + { ++ setInProgress= 0x0, + bootInfo = 0x4, + bootFlags = 0x5, + opalNetworkSettings = 0x61 +@@ -53,6 +55,7 @@ enum class BootOptionParameter : size_t + + enum class BootOptionResponseSize : size_t + { ++ setInProgress = 3, + bootFlags = 5, + opalNetworkSettings = 50 + }; +-- +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..bc8c72f13 --- /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 1c8cb6b7c99ad85f470aa87095fcfb4de822ddb1 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 1/1] 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 61065ad..59e38ea 100644 +--- a/transporthandler.cpp ++++ b/transporthandler.cpp +@@ -1469,8 +1469,298 @@ RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly, + } // 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, +@@ -1479,4 +1769,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..61ac5fede --- /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 b5400c4bc756a800fbeb4cc53117956fb59dc57d 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 1/1] 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 59e38ea..b64953f 100644 +--- a/transporthandler.cpp ++++ b/transporthandler.cpp +@@ -1559,6 +1559,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; +@@ -1761,6 +1783,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, +@@ -1773,4 +1960,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..96e45cad3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0062-Update-IPMI-Chassis-Control-command.patch @@ -0,0 +1,291 @@ +From 92bdbcd90a445e49ba2f8e483d44ec41152d32b4 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 | 206 +++++++++++++---------------------------------------- + 1 file changed, 50 insertions(+), 156 deletions(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 0326806..8bfab88 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -31,6 +31,7 @@ + #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp> + #include <xyz/openbmc_project/Control/Boot/Source/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> + +@@ -717,59 +718,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; + } + + //------------------------------------------ +@@ -1071,76 +1076,6 @@ ipmi::RspType<bool, // Power is on + diagButtonDisableAllow, sleepButtonDisableAllow); + } + +-//------------------------------------------------------------- +-// 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 +@@ -1153,63 +1088,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 = 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.7.4 + 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..aac0850ea --- /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 d9c89943d7b0aa00ee99b7c11278ac272a47a790 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 2ffaae3..e9b7a9c 100644 +--- a/app/watchdog.cpp ++++ b/app/watchdog.cpp +@@ -81,6 +81,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 wdTimerUseMask = 0x7; + static constexpr uint8_t wdTimerUseResTimer1 = 0x0; +@@ -130,6 +131,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, +@@ -257,6 +297,13 @@ ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + // 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_CC_OK; + } +diff --git a/app/watchdog_service.cpp b/app/watchdog_service.cpp +index 77663b4..0c4ea28 100644 +--- a/app/watchdog_service.cpp ++++ b/app/watchdog_service.cpp +@@ -203,3 +203,9 @@ void WatchdogService::setTimeRemaining(uint64_t timeRemaining) + { + setProperty("TimeRemaining", timeRemaining); + } ++ ++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 ed64a3c..b550f37 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; + +@@ -99,6 +101,13 @@ class WatchdogService + */ + void setTimeRemaining(uint64_t timeRemaining); + ++ /** @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.7.4 + 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..a3debae71 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend @@ -0,0 +1,40 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +PROJECT_SRC_DIR := "${THISDIR}/${PN}" + +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://0056-add-SetInProgress-to-get-set-boot-option-cmd.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 \ + " +FILES_${PN}_remove = " \ + ${systemd_unitdir}/system/obmc-host-shutdown@0.target.requires/ \ + ${systemd_unitdir}/system/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/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..33392f3c1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend @@ -0,0 +1,11 @@ +SRC_URI = "git://github.com/openbmc/ipmbbridge.git" +SRCREV = "8fe0abe6d9f69f735e93d7055687fce4b56e80bf" +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI += "file://0001-Add-dbus-method-SlotIpmbRequest.patch \ + file://ipmb-channels.json \ + " + +do_install_append() { + install -D ${WORKDIR}/ipmb-channels.json \ + ${D}/usr/share/ipmbbridge +}
\ No newline at end of file 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..0aa63ca03 --- /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 = "4a4d1d03d99fabe089e649aa226ad4c61e71684e" + +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/0009-Add-dbus-interface-for-sol-commands.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0009-Add-dbus-interface-for-sol-commands.patch new file mode 100644 index 000000000..5f749af45 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0009-Add-dbus-interface-for-sol-commands.patch @@ -0,0 +1,317 @@ +From e5ab844259f569656e95f5324f7428229dd811a7 Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@intel.com> +Date: Wed, 3 Jul 2019 07:39:47 +0800 +Subject: [PATCH] Add dbus interface for sol commands + +Add dbus interface for sol config parameters so that after move set/get +sol config parameter command from net-ipmid to host-ipmid, the command +can send config parameters to net-ipmid sol service through the dbus +interface. + +Tested by: +busctl introspect xyz.openbmc_project.Settings /xyz/openbmc_project +/network/host0/sol can show correct dbus properties of sol parameters. +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x00 0x01 +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x01 0x00 +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x02 0x83 +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x03 0x5 0x03 +ipmitool -I lanplus -H x -U x -P x raw 0x0c 0x21 0x0e 0x04 0x5 0x03 +all these commands can change the dbus properties as the value in +above commands. +Before and after run these commands, ipmitool -I lanplus -H x -U x +-P x sol activate can start sol session correctly. +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> +--- + command/payload_cmds.cpp | 3 + + command/sol_cmds.cpp | 84 -------------------------- + sol/sol_manager.cpp | 124 +++++++++++++++++++++++++++++++++++++++ + sol/sol_manager.hpp | 1 + + sol_module.cpp | 6 -- + 5 files changed, 128 insertions(+), 90 deletions(-) + +diff --git a/command/payload_cmds.cpp b/command/payload_cmds.cpp +index c8e682e..bc987c5 100644 +--- a/command/payload_cmds.cpp ++++ b/command/payload_cmds.cpp +@@ -41,6 +41,9 @@ std::vector<uint8_t> activatePayload(const std::vector<uint8_t>& inPayload, + return outPayload; + } + ++ std::get<sol::Manager&>(singletonPool) ++ .updateSOLParameter(ipmi::convertCurrentChannelNum( ++ ipmi::currentChNum, getInterfaceIndex())); + if (!std::get<sol::Manager&>(singletonPool).enable) + { + response->completionCode = IPMI_CC_PAYLOAD_TYPE_DISABLED; +diff --git a/command/sol_cmds.cpp b/command/sol_cmds.cpp +index fda3e91..a1e820f 100644 +--- a/command/sol_cmds.cpp ++++ b/command/sol_cmds.cpp +@@ -71,90 +71,6 @@ void activating(uint8_t payloadInstance, uint32_t sessionID) + outPayload); + } + +-std::vector<uint8_t> setConfParams(const std::vector<uint8_t>& inPayload, +- const message::Handler& handler) +-{ +- std::vector<uint8_t> outPayload(sizeof(SetConfParamsResponse)); +- auto request = +- reinterpret_cast<const SetConfParamsRequest*>(inPayload.data()); +- auto response = reinterpret_cast<SetConfParamsResponse*>(outPayload.data()); +- response->completionCode = IPMI_CC_OK; +- +- switch (static_cast<Parameter>(request->paramSelector)) +- { +- case Parameter::PROGRESS: +- { +- uint8_t progress = request->value & progressMask; +- std::get<sol::Manager&>(singletonPool).progress = progress; +- break; +- } +- case Parameter::ENABLE: +- { +- bool enable = request->value & enableMask; +- std::get<sol::Manager&>(singletonPool).enable = enable; +- break; +- } +- case Parameter::AUTHENTICATION: +- { +- if (!request->auth.auth || !request->auth.encrypt) +- { +- response->completionCode = ipmiCCWriteReadParameter; +- } +- else if (request->auth.privilege < +- static_cast<uint8_t>(session::Privilege::USER) || +- request->auth.privilege > +- static_cast<uint8_t>(session::Privilege::OEM)) +- { +- response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; +- } +- else +- { +- std::get<sol::Manager&>(singletonPool).solMinPrivilege = +- static_cast<session::Privilege>(request->auth.privilege); +- } +- break; +- } +- case Parameter::ACCUMULATE: +- { +- using namespace std::chrono_literals; +- +- if (request->acc.threshold == 0) +- { +- response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; +- break; +- } +- +- std::get<sol::Manager&>(singletonPool).accumulateInterval = +- request->acc.interval * sol::accIntervalFactor * 1ms; +- std::get<sol::Manager&>(singletonPool).sendThreshold = +- request->acc.threshold; +- break; +- } +- case Parameter::RETRY: +- { +- using namespace std::chrono_literals; +- +- std::get<sol::Manager&>(singletonPool).retryCount = +- request->retry.count; +- std::get<sol::Manager&>(singletonPool).retryInterval = +- request->retry.interval * sol::retryIntervalFactor * 1ms; +- break; +- } +- case Parameter::PORT: +- { +- response->completionCode = ipmiCCWriteReadParameter; +- break; +- } +- case Parameter::NVBITRATE: +- case Parameter::VBITRATE: +- case Parameter::CHANNEL: +- default: +- response->completionCode = ipmiCCParamNotSupported; +- } +- +- return outPayload; +-} +- + std::vector<uint8_t> getConfParams(const std::vector<uint8_t>& inPayload, + const message::Handler& handler) + { +diff --git a/sol/sol_manager.cpp b/sol/sol_manager.cpp +index a118457..55d269a 100644 +--- a/sol/sol_manager.cpp ++++ b/sol/sol_manager.cpp +@@ -14,6 +14,11 @@ + #include <cmath> + #include <ipmid/utils.hpp> + #include <phosphor-logging/log.hpp> ++#include <sdbusplus/message/types.hpp> ++ ++constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL"; ++constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/"; ++constexpr const char* PROP_INTF = "org.freedesktop.DBus.Properties"; + + namespace sol + { +@@ -103,6 +108,125 @@ void Manager::stopHostConsole() + } + } + ++std::string getService(sdbusplus::bus::bus& bus, const std::string& intf, ++ const std::string& path) ++{ ++ auto mapperCall = ++ bus.new_method_call("xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject"); ++ ++ mapperCall.append(path); ++ mapperCall.append(std::vector<std::string>({intf})); ++ ++ std::map<std::string, std::vector<std::string>> mapperResponse; ++ ++ try ++ { ++ auto mapperResponseMsg = bus.call(mapperCall); ++ mapperResponseMsg.read(mapperResponse); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ throw std::runtime_error("ERROR in mapper call"); ++ } ++ ++ if (mapperResponse.begin() == mapperResponse.end()) ++ { ++ throw std::runtime_error("ERROR in reading the mapper response"); ++ } ++ ++ return mapperResponse.begin()->first; ++} ++ ++ipmi::PropertyMap getAllDbusProperties(sdbusplus::bus::bus& bus, ++ const std::string& service, ++ const std::string& objPath, ++ const std::string& interface) ++{ ++ ipmi::PropertyMap properties; ++ ++ sdbusplus::message::message method = bus.new_method_call( ++ service.c_str(), objPath.c_str(), PROP_INTF, "GetAll"); ++ ++ method.append(interface); ++ ++ try ++ { ++ sdbusplus::message::message reply = bus.call(method); ++ reply.read(properties); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Failed to get all properties", ++ phosphor::logging::entry("PATH=%s", objPath.c_str()), ++ phosphor::logging::entry("INTERFACE=%s", interface.c_str())); ++ throw std::runtime_error("ERROR in reading proerties"); ++ } ++ ++ return properties; ++} ++ ++void Manager::updateSOLParameter(uint8_t channelNum) ++{ ++ std::variant<uint8_t, bool> value; ++ sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); ++ static std::string solService{}; ++ ipmi::PropertyMap properties; ++ std::string ethdevice = ipmi::getChannelName(channelNum); ++ std::string solPathWitheEthName = solPath + ethdevice; ++ if (solService.empty()) ++ { ++ try ++ { ++ solService = getService(dbus, solInterface, solPathWitheEthName); ++ } ++ catch (const std::runtime_error& e) ++ { ++ solService.clear(); ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error: get SOL service failed"); ++ return; ++ } ++ } ++ try ++ { ++ properties = getAllDbusProperties(dbus, solService, solPathWitheEthName, ++ solInterface); ++ } ++ catch (const std::runtime_error&) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error setting sol parameter"); ++ return; ++ } ++ ++ progress = std::get<uint8_t>(properties["Progress"]); ++ ++ 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["Privilege"])); ++ ++ accumulateInterval = ++ std::get<uint8_t>((properties["AccumulateIntervalMS"])) * ++ sol::accIntervalFactor * 1ms; ++ ++ sendThreshold = std::get<uint8_t>(properties["Threshold"]); ++ ++ retryCount = std::get<uint8_t>(properties["RetryCount"]); ++ ++ retryInterval = std::get<uint8_t>(properties["RetryIntervalMS"]) * ++ sol::retryIntervalFactor * 1ms; ++ ++ return; ++} ++ + void Manager::startPayloadInstance(uint8_t payloadInstance, + session::SessionID sessionID) + { +diff --git a/sol/sol_manager.hpp b/sol/sol_manager.hpp +index 5b48add..4e797d4 100644 +--- a/sol/sol_manager.hpp ++++ b/sol/sol_manager.hpp +@@ -252,6 +252,7 @@ class Manager + * @return 0 on success and errno on failure. + */ + int writeConsoleSocket(const std::vector<uint8_t>& input) const; ++ void updateSOLParameter(uint8_t channelNum); + + private: + SOLPayloadMap payloadMap; +diff --git a/sol_module.cpp b/sol_module.cpp +index 8200e74..2b1fb46 100644 +--- a/sol_module.cpp ++++ b/sol_module.cpp +@@ -42,12 +42,6 @@ void registerCommands() + &getPayloadInfo, + session::Privilege::USER, + false}, +- // Set SOL Configuration Parameters +- {{(static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | +- static_cast<uint16_t>(::command::NetFns::TRANSPORT) | 0x21}, +- &setConfParams, +- session::Privilege::ADMIN, +- false}, + // Get SOL Configuration Parameters + {{(static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | + static_cast<uint16_t>(::command::NetFns::TRANSPORT) | 0x22}, +-- +2.17.1 + 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..da173704b --- /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,336 @@ +From a36f181163974b2da0a954fc97a89fb2cdbd7287 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 | 91 ---------------------------- + command/sol_cmds.hpp | 168 --------------------------------------------------- + sol_module.cpp | 6 -- + 3 files changed, 265 deletions(-) + +diff --git a/command/sol_cmds.cpp b/command/sol_cmds.cpp +index 804b5ea..8b2d041 100644 +--- a/command/sol_cmds.cpp ++++ b/command/sol_cmds.cpp +@@ -65,97 +65,6 @@ void activating(uint8_t payloadInstance, uint32_t sessionID) + outPayload); + } + +-std::vector<uint8_t> getConfParams(const std::vector<uint8_t>& inPayload, +- const 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( +- std::get<sol::Manager&>(singletonPool).progress); +- break; +- } +- case Parameter::ENABLE: +- { +- outPayload.push_back(std::get<sol::Manager&>(singletonPool).enable); +- break; +- } +- case Parameter::AUTHENTICATION: +- { +- Auth value{0}; +- +- value.encrypt = std::get<sol::Manager&>(singletonPool).forceEncrypt; +- value.auth = std::get<sol::Manager&>(singletonPool).forceAuth; +- value.privilege = static_cast<uint8_t>( +- std::get<sol::Manager&>(singletonPool).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 = std::get<sol::Manager&>(singletonPool) +- .accumulateInterval.count() / +- sol::accIntervalFactor; +- value.threshold = +- std::get<sol::Manager&>(singletonPool).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 = std::get<sol::Manager&>(singletonPool).retryCount; +- value.interval = +- std::get<sol::Manager&>(singletonPool).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( +- std::get<sol::Manager&>(singletonPool).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 182b73e..10cbf25 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, +- const 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, +- const message::Handler& handler); +- + } // namespace command + + } // namespace sol +diff --git a/sol_module.cpp b/sol_module.cpp +index 2b1fb46..6da82c0 100644 +--- a/sol_module.cpp ++++ b/sol_module.cpp +@@ -42,12 +42,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.7.4 + 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..4c00421a9 --- /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 = "07bb095158b39cedb49dae0972e489a6a2776faf" + +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://0009-Add-dbus-interface-for-sol-commands.patch \ + file://0011-Remove-Get-SOL-Config-Command-from-Netipmid.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..72d991c7e --- /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..30a229022 --- /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 = "403434f80e6a6c476516848dde2512b37f7ec5d8" +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..a1d20c2bc --- /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..dd48df0c6 --- /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 native +inherit obmc-phosphor-utils + +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/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..c47a581f6 --- /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/peci/peci-pcie_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/peci/peci-pcie_%.bbappend new file mode 100644 index 000000000..71a6d58da --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/peci/peci-pcie_%.bbappend @@ -0,0 +1,3 @@ +SRC_URI = "git://github.com/openbmc/peci-pcie" + +SRCREV = "9fa54b52f83c00fd713085e6849d3f261672d008" 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..f1327dbbd --- /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 = "6707fc81f48634599df3fce764578d6d9661881f" + +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..68b43132d --- /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 = "46651f2a88322127cbd979d71c616fd35df8d989" + +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..317386460 --- /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 = "0f98e0d45a725003b810ea06f8e5f032b2864a5c" + +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..90d8bc709 --- /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 = "0f98e0d45a725003b810ea06f8e5f032b2864a5c" + +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..55fda9954 --- /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 = "0f98e0d45a725003b810ea06f8e5f032b2864a5c" + +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..b9c2eef31 --- /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 = "0f98e0d45a725003b810ea06f8e5f032b2864a5c" + +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/pldmd.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pldmd.bb new file mode 100644 index 000000000..b047c0f85 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pldmd.bb @@ -0,0 +1,30 @@ +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 = "0f98e0d45a725003b810ea06f8e5f032b2864a5c" + +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 \ + " + +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..a76ebe7ca --- /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 = "0f98e0d45a725003b810ea06f8e5f032b2864a5c" + +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..410775ee3 --- /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..245dabe6c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts/init @@ -0,0 +1,263 @@ +#!/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 + # 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..7138e8628 --- /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 = "ea03e4e87f5d5f0d873624b46ebc3deabb8d6ebe" + +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..3fab0ae54 --- /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..e96f5646c --- /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 = "e526b86d7f9eef3b7a58f2800263666a04051239" + 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/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..9a50b255d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend @@ -0,0 +1,39 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +PROJECT_SRC_DIR := "${THISDIR}/${PN}" + +SRCREV = "6736d4b2a77cec00a8919f26035176c8b8025a4d" +#SRC_URI = "git://github.com/openbmc/dbus-sensors.git" + +SRC_URI += "\ + file://intrusionsensor-depend-on-networkd.conf \ + " + +DEPENDS_append = " libgpiod libmctp" + +PACKAGECONFIG += " \ + adcsensor \ + cpusensor \ + exitairtempsensor \ + fansensor \ + hwmontempsensor \ + intrusionsensor \ + ipmbsensor \ + mcutempsensor \ + psusensor \ +" + +PACKAGECONFIG[nvmesensor] = "-DDISABLE_NVME=OFF, -DDISABLE_NVME=ON" + +SYSTEMD_SERVICE_${PN} += "${@bb.utils.contains('PACKAGECONFIG', 'nvmesensor', \ + 'xyz.openbmc_project.nvmesensor.service', \ + '', d)}" + +PACKAGECONFIG_remove = "nvmesensor" + +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..f41bfd6d5 --- /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 = "4879d875dc197a19e15227f45630a409a52330c4" +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..85e3d6e8b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/special-mode-mgr/special-mode-mgr_git.bb @@ -0,0 +1,30 @@ +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 = "024531a11dbe2bd13f2f6e59bdb41a22fce8b431" + +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 \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-logging \ + " 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..d23480a05 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +DEPENDS += "gtest" + +SYSTEMD_SERVICE_${PN}-bmc += "obmc-mapper.target" 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..0dae2be3c --- /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 = "291d6388e0b770e89091935bc4edc7f371874666" + +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..04ae511ef --- /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 = "503c158972ff74a23ead8f50138107157b46758d" + +EXTRA_OEMESON += " -Dmax-reports=5" +EXTRA_OEMESON += " -Dmax-reading-parameters=200" +EXTRA_OEMESON += " -Dmin-interval=5000" 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..2182201cf --- /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,1658 @@ +From ab2ff3d715b00a13b0a670fb010793dc626352e0 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> +--- + Makefile.am | 5 +- + mainapp.cpp | 89 ++++++- + user_mgr.cpp | 297 ++------------------- + user_mgr.hpp | 9 +- + user_service.cpp | 786 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + user_service.hpp | 233 +++++++++++++++++ + 6 files changed, 1145 insertions(+), 274 deletions(-) + create mode 100644 user_service.cpp + create mode 100644 user_service.hpp + +diff --git a/Makefile.am b/Makefile.am +index 7c7271e..58916b0 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 c9da030..03c406a 100644 +--- a/mainapp.cpp ++++ b/mainapp.cpp +@@ -14,18 +14,105 @@ + * limitations under the License. + */ + #include <string> ++#include <iostream> ++#include <getopt.h> + #include "user_mgr.hpp" ++#include "user_service.hpp" + #include "config.h" + + // 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 9694fd1..c48196a 100644 +--- a/user_mgr.cpp ++++ b/user_mgr.cpp +@@ -14,27 +14,20 @@ + // limitations under the License. + */ + +-#include <shadow.h> +-#include <unistd.h> +-#include <sys/types.h> +-#include <sys/wait.h> + #include <time.h> ++#include <cstdio> + #include <fstream> + #include <grp.h> + #include <pwd.h> + #include <regex> +-#include <algorithm> +-#include <numeric> +-#include <boost/process/child.hpp> +-#include <boost/process/io.hpp> + #include <boost/algorithm/string/split.hpp> + #include <xyz/openbmc_project/Common/error.hpp> + #include <xyz/openbmc_project/User/Common/error.hpp> + #include <phosphor-logging/log.hpp> + #include <phosphor-logging/elog.hpp> + #include <phosphor-logging/elog-errors.hpp> ++#include <stdexcept> + #include "shadowlock.hpp" +-#include "file.hpp" + #include "user_mgr.hpp" + #include "users.hpp" + #include "config.h" +@@ -44,12 +37,10 @@ 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; +@@ -95,79 +86,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()) +@@ -294,39 +212,14 @@ void UserMgr::createUser(std::string userName, + { + throwForInvalidPrivilege(priv); + throwForInvalidGroups(groupNames); +- // All user management lock has to be based on /etc/shadow +- 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( +@@ -340,19 +233,11 @@ void UserMgr::createUser(std::string userName, + + void UserMgr::deleteUser(std::string userName) + { +- // All user management lock has to be based on /etc/shadow +- 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); + +@@ -363,24 +248,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 +- 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(); +@@ -404,8 +278,6 @@ void UserMgr::updateGroupsAndPriv(const std::string &userName, + { + throwForInvalidPrivilege(priv); + throwForInvalidGroups(groupNames); +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserDoesNotExist(userName); + const std::vector<std::string> &oldGroupNames = + usersList[userName].get()->userGroups(); +@@ -421,29 +293,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())); +@@ -639,19 +490,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 +- 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()), +@@ -773,54 +614,8 @@ bool UserMgr::userPasswordExpired(const std::string &userName) + + UserSSHLists UserMgr::getUserAndSshGrpList() + { +- // All user management lock has to be based on /etc/shadow +- 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() +@@ -831,49 +626,14 @@ size_t UserMgr::getIpmiUsersCount() + + bool UserMgr::isUserEnabled(const std::string &userName) + { +- // All user management lock has to be based on /etc/shadow +- 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) +@@ -1104,11 +864,9 @@ void UserMgr::initUserObjects(void) + { + // All user management lock has to be based on /etc/shadow + 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()) + { +@@ -1163,8 +921,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()); +@@ -1272,6 +1032,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 e25ca87..262aaf9 100644 +--- a/user_mgr.hpp ++++ b/user_mgr.hpp +@@ -21,6 +21,7 @@ + #include <unordered_map> + #include <variant> + #include "users.hpp" ++#include "user_service.hpp" + + namespace phosphor + { +@@ -28,8 +29,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; + +@@ -76,8 +75,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 +@@ -193,6 +194,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..c3c45bd +--- /dev/null ++++ b/user_service.cpp +@@ -0,0 +1,786 @@ ++/* ++// 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 <grp.h> ++#include <pwd.h> ++#include <numeric> ++#include <boost/process/child.hpp> ++#include <boost/process/io.hpp> ++#include <boost/algorithm/string/split.hpp> ++#include "shadowlock.hpp" ++#include "file.hpp" ++#include "user_service.hpp" ++ ++/* 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 ++ 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 ++ 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 ++ 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 ++ 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 ++ 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 ++ 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 ++ 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..97a049b +--- /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 <xyz/openbmc_project/Common/error.hpp> ++#include <xyz/openbmc_project/User/Common/error.hpp> ++#include <phosphor-logging/log.hpp> ++#include <phosphor-logging/elog.hpp> ++#include <boost/process/child.hpp> ++#include <boost/process/io.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.7.4 + 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..2abfeb78c --- /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 a78bad158bca59dadb93c9c52d6daefa1c52b9cf 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] 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 c3c45bd..0a6b171 100644 +--- a/user_service.cpp ++++ b/user_service.cpp +@@ -143,28 +143,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.7.4 + 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..2b90b01e5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend @@ -0,0 +1,11 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI = "git://github.com/openbmc/phosphor-user-manager" +SRCREV = "703131fa01422bc28675f5d629edb98d285a1bc4" + +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 \ + " 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..e568ea5d2 --- /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/frb2-watchdog.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb new file mode 100644 index 000000000..d6ff9f7a4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb @@ -0,0 +1,33 @@ + +SUMMARY = "FRB2 timer service" +DESCRIPTION = "The FRB2 timer service will monitor the mailbox register 0\ +and start a watchdog for FRB2 if the data is 1(BIOS will write this value)" + +SRC_URI = "\ + file://CMakeLists.txt \ + file://frb2-watchdog.cpp \ + " +PV = "0.1" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +S = "${WORKDIR}" + +inherit cmake +inherit pkgconfig + +DEPENDS += " \ + systemd \ + sdbusplus \ + phosphor-logging \ + phosphor-dbus-interfaces \ + boost \ + " + +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-logging \ + phosphor-dbus-interfaces \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format new file mode 100644 index 000000000..dd2770837 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.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/watchdog/frb2-watchdog/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt new file mode 100644 index 000000000..bd5567d31 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required (VERSION 3.5 FATAL_ERROR) +project (frb2-watchdog 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 libsystemd +find_package (PkgConfig REQUIRED) +pkg_check_modules (SYSTEMD libsystemd REQUIRED) +include_directories (${SYSTEMD_INCLUDE_DIRS}) +link_directories (${SYSTEMD_LIBRARY_DIRS}) + +# 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}) + +# import phosphor-dbus-interfaces +find_package (PkgConfig REQUIRED) +pkg_check_modules (DBUSINTERFACE phosphor-dbus-interfaces REQUIRED) +include_directories (${DBUSINTERFACE_INCLUDE_DIRS}) +link_directories (${DBUSINTERFACE_LIBRARY_DIRS}) + +add_executable (frb2-watchdog frb2-watchdog.cpp) + +target_link_libraries (${PROJECT_NAME} systemd) +target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES}) +target_link_libraries (${PROJECT_NAME} ${SDBUSPLUSPLUS_LIBRARIES}) +target_link_libraries (${PROJECT_NAME} ${DBUSINTERFACE_LIBRARIES} + phosphor_logging) +install (TARGETS frb2-watchdog DESTINATION bin) diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json new file mode 100644 index 000000000..583c255a3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/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/watchdog/frb2-watchdog/frb2-watchdog.cpp b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp new file mode 100644 index 000000000..bae54f335 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp @@ -0,0 +1,264 @@ +/* Copyright 2018 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 <stdlib.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <boost/asio/buffers_iterator.hpp> +#include <boost/asio/deadline_timer.hpp> +#include <boost/asio/io_service.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/posix/stream_descriptor.hpp> +#include <boost/asio/read_until.hpp> +#include <boost/asio/streambuf.hpp> +#include <boost/container/flat_set.hpp> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <iostream> +#include <memory> +#include <optional> +#include <phosphor-logging/log.hpp> +#include <sdbusplus/asio/object_server.hpp> +#include <sdbusplus/bus.hpp> +#include <sdbusplus/bus/match.hpp> +#include <sdbusplus/message.hpp> +#include <sdbusplus/timer.hpp> +#include <vector> +#include <xyz/openbmc_project/State/Watchdog/server.hpp> + +void handleResponse(const boost::system::error_code &err, + std::size_t bytes_transferred); + +static int mailboxDevFd = -1; + +static boost::asio::io_service io; +static auto conn = std::make_shared<sdbusplus::asio::connection>(io); +boost::asio::ip::tcp::socket mailBoxDevSocket(io); +boost::asio::deadline_timer pollTimer(io); +boost::asio::posix::stream_descriptor inputDevice(io); + +// mailbox registre data[0:0] for FRB2 enable bit +boost::asio::streambuf readBuf(1); +std::string dataRead; + +// FRB2 watchdog timeout is 6 minutes +static constexpr unsigned int frb2TimerIntervalMs = 360 * 1000; + +// mailbox device polling time interval is 2 seconds +static constexpr unsigned int pollMs = 2000; + +static constexpr unsigned int frb2Started = 1; +static constexpr unsigned int frb2Stopped = 0; + +// FRB2 status +static uint8_t frb2Status = frb2Stopped; + +static constexpr const char *mailboxDevName = "/dev/aspeed-mbox"; + +static constexpr const char frb2Bus[] = "xyz.openbmc_project.FRB2"; +static constexpr const char frb2Obj[] = "/xyz/openbmc_project/FRB2"; +static constexpr const char frb2Intf[] = "xyz.openbmc_project.FRB2"; + +static constexpr char powerBus[] = "xyz.openbmc_project.Chassis.Control.Power"; +static constexpr char powerPath[] = + "/xyz/openbmc_project/Chassis/Control/Power0"; +static constexpr char powerIntf[] = "xyz.openbmc_project.Chassis.Control.Power"; + +static constexpr char wdBus[] = "xyz.openbmc_project.Watchdog"; +static constexpr char wdPath[] = "/xyz/openbmc_project/watchdog/host0"; +static constexpr char wdIntf[] = "xyz.openbmc_project.State.Watchdog"; +static constexpr char propIntf[] = "org.freedesktop.DBus.Properties"; + +typedef boost::asio::buffers_iterator<boost::asio::const_buffers_1> iterator; + +// check if FRB2 bit is 0x1 +std::pair<iterator, bool> matchFRB2(iterator begin, iterator end) +{ + unsigned char ch = 0; + iterator i = begin; + + while (i != end) + { + ch = static_cast<unsigned char>(*i); + if (ch & 0x1) + { + return std::make_pair(i, true); + } + i++; + } + + return std::make_pair(i, false); +} + +static void startRead() +{ + boost::asio::async_read_until(inputDevice, readBuf, matchFRB2, + [&](const boost::system::error_code &ec, + std::size_t bytes_transferred) { + handleResponse(ec, bytes_transferred); + }); +} + +template <typename T> void setProperty(const std::string &key, const T &val) +{ + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "setProperty", phosphor::logging::entry("KEY=%s", key.c_str())); + + try + { + conn->async_method_call( + [](const boost::system::error_code &err) { + if (err) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "async_method_call error!", + phosphor::logging::entry( + "ERROR=%s", + boost::system::system_error(err).what())); + } + }, + wdBus, wdPath, propIntf, "Set", wdIntf, key, std::variant<T>(val)); + } + catch (sdbusplus::exception::SdBusError &e) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Dbus error!", phosphor::logging::entry("ERROR=%s", e.what())); + } +} +void handleResponse(const boost::system::error_code &err, + std::size_t bytes_transferred) +{ + std::istream responseStream(&readBuf); + std::string response; + int n = 0; + uint64_t interval = frb2TimerIntervalMs; + + std::getline(responseStream, response); + responseStream.clear(); + + if (err == boost::system::errc::bad_file_descriptor) + { + + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "bad file descriptor"); + return; // we're being destroyed + } + + if (!err) + { + // FRB2 is set by BIOS + if (frb2Stopped == frb2Status) + { + // start FRB2 watchdog + frb2Status = frb2Started; + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "FRB2 enable, start FRB2 watchdog"); + setProperty( + "ExpireAction", + std::string( + "xyz.openbmc_project.State.Watchdog.Action.HardReset")); + setProperty("Interval", interval); + setProperty("TimeRemaining", interval); + setProperty("Initialized", true); + setProperty("Enabled", true); + } + } + else if (err == boost::asio::error::misc_errors::not_found) + { + // FRB2 is clear, stop FRB2 watchdog if it is started + if (frb2Started == frb2Status) + { + frb2Status = frb2Stopped; + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "FRB2 is unset, stop FRB2 watchdog"); + setProperty("Enabled", false); + } + } + else + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "handleResponse error!", + phosphor::logging::entry("ERROR=%s", + boost::system::system_error(err).what())); + } + + pollTimer.expires_from_now(boost::posix_time::milliseconds(pollMs)); + pollTimer.async_wait( + [](const boost::system::error_code &ec) { startRead(); }); +} + +int main(int argc, char **argv) +{ + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "Monitor FRB2 signal"); + + sdbusplus::bus::match_t biosPostSignal( + static_cast<sdbusplus::bus::bus &>(*conn), + sdbusplus::bus::match::rules::type::signal() + + sdbusplus::bus::match::rules::member("PostCompleted") + + sdbusplus::bus::match::rules::path(powerPath) + + sdbusplus::bus::match::rules::interface(powerIntf), + [](sdbusplus::message::message &msg) { + uint8_t value = 0; + ssize_t rc = 0; + phosphor::logging::log<phosphor::logging::level::INFO>( + "BIOS post completed signal"); + // stop FRB2 and clean mailbox + value = 0; + rc = ::pwrite(mailboxDevFd, &value, 1, 0); + if (rc != 1) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "mailbox write error!"); + } + setProperty("Enabled", false); + frb2Status = frb2Stopped; + return; + }); + + conn->request_name(frb2Bus); + + auto server = sdbusplus::asio::object_server(conn); + + std::shared_ptr<sdbusplus::asio::dbus_interface> frb2Iface = + server.add_interface(frb2Obj, frb2Intf); + + frb2Iface->register_property("frb2Status", frb2Status); + + frb2Iface->initialize(); + + mailboxDevFd = ::open(mailboxDevName, O_RDWR | O_CLOEXEC); + if (mailboxDevFd < 0) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "mailbox device open fail!"); + return -1; + } + + inputDevice.assign(mailboxDevFd); + + startRead(); + + io.run(); + + ::close(mailboxDevFd); + + return 0; +} 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..360ba35f0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch @@ -0,0 +1,336 @@ +From 82f31d1e6096acd4f223f0b0fe0d814c27450022 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 + +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> +--- + watchdog.cpp | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- + watchdog.hpp | 23 ++++++- + 2 files changed, 226 insertions(+), 10 deletions(-) + +diff --git a/watchdog.cpp b/watchdog.cpp +index 9090760..079d88e 100644 +--- a/watchdog.cpp ++++ b/watchdog.cpp +@@ -1,11 +1,14 @@ + #include "watchdog.hpp" + ++#include <systemd/sd-journal.h> ++ + #include <algorithm> + #include <chrono> + #include <phosphor-logging/elog.hpp> + #include <phosphor-logging/log.hpp> + #include <sdbusplus/exception.hpp> + #include <xyz/openbmc_project/Common/error.hpp> ++#include <xyz/openbmc_project/State/Host/server.hpp> + + namespace phosphor + { +@@ -18,10 +21,77 @@ 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 ++ ++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) + { +@@ -102,13 +172,102 @@ 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); ++ } + + auto target = actionTargetMap.find(action); + if (target == actionTargetMap.end()) +@@ -128,10 +287,23 @@ 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) ++ { ++ 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)); ++ } ++ else ++ { ++ 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); + } +@@ -142,6 +314,29 @@ void Watchdog::timeOutHandler() + entry("ERROR=%s", e.what())); + commit<InternalFailure>(); + } ++ ++ // set restart cause for watchdog HardReset & PowerCycle actions ++ if ((action == Watchdog::Action::HardReset) || ++ (action == Watchdog::Action::PowerCycle)) ++ { ++ try ++ { ++ 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(method); ++ } ++ catch (sdbusplus::exception_t& e) ++ { ++ log<level::ERR>("Failed to set HostRestartCause property", ++ entry("ERROR=%s", e.what())); ++ commit<InternalFailure>(); ++ } ++ } + } + + tryFallbackOrDisable(); +diff --git a/watchdog.hpp b/watchdog.hpp +index 7de9bb3..b004b7a 100644 +--- a/watchdog.hpp ++++ b/watchdog.hpp +@@ -68,7 +68,18 @@ class Watchdog : public WatchdogInherits + WatchdogInherits(bus, objPath), + bus(bus), actionTargetMap(std::move(actionTargetMap)), + fallback(std::move(fallback)), minInterval(minInterval), +- timer(event, std::bind(&Watchdog::timeOutHandler, this)) ++ timer(event, std::bind(&Watchdog::timeOutHandler, this)), ++ 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); ++ }) + { + // We set the watchdog interval with the default value. + interval(interval()); +@@ -77,6 +88,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. + * +@@ -165,6 +182,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.7.4 + 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..007e39d8a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service @@ -0,0 +1,16 @@ +[Unit] +Description=Phosphor Watchdog + +[Service] +ExecStart=/usr/bin/env 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 + +SyslogIdentifier=phosphor-watchdog +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..f0b8e8f23 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend @@ -0,0 +1,9 @@ +FILESEXTRAPATHS_append := ":${THISDIR}/${PN}" + +SRCREV = "c35135d32f9cb84b62de7b72eee3a2e87b4b3d4d" +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..addd1ccb2 --- /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..2dfe8544f --- /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 = "3b13f734a5f881b9b51346ba09fabea752b145f3" 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..7201eccd9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue_%.bbappend @@ -0,0 +1,7 @@ +# Enable downstream autobump +SRC_URI = "git://github.com/openbmc/webui-vue.git" +SRCREV = "5fe1c3fed73164d4fe82ebb142cefbca72c2e706" + +do_compile_prepend() { + cp -vf ${S}/.env.intel ${S}/.env +} |