├── .build.sh ├── .clang-format ├── .clang-tidy ├── .gitignore ├── .shellcheck ├── .travis.yml ├── LICENSE ├── OWNERS ├── README.md ├── app ├── channel.cpp ├── channel.hpp ├── meson.build ├── watchdog.cpp ├── watchdog.hpp ├── watchdog_service.cpp └── watchdog_service.hpp ├── apphandler.cpp ├── apphandler.hpp ├── chassishandler.cpp ├── chassishandler.hpp ├── dbus-sdr ├── meson.build ├── sdrutils.cpp ├── sensorcommands.cpp ├── sensorutils.cpp └── storagecommands.cpp ├── dcmihandler.cpp ├── dcmihandler.hpp ├── docs ├── configuration.md ├── contributing.md ├── ipmi-network-format.md ├── ipmitool-commands-cheatsheet.md ├── oem-extension-numbering.md └── testing.md ├── error-HostEvent.hpp ├── fruread.hpp ├── generate_whitelist.sh ├── generate_whitelist_create.sh ├── globalhandler.cpp ├── groupext.cpp ├── host-cmd-manager.cpp ├── host-cmd-manager.hpp ├── host-interface.cpp ├── host-interface.hpp ├── host-ipmid-whitelist.conf ├── include ├── dbus-sdr │ ├── sdrutils.hpp │ ├── sensorcommands.hpp │ ├── sensorutils.hpp │ └── storagecommands.hpp ├── ipmid-host │ ├── cmd-utils.hpp │ └── cmd.hpp ├── ipmid │ ├── api-types.hpp │ ├── api.h │ ├── api.hpp │ ├── entity_map_json.hpp │ ├── filter.hpp │ ├── handler.hpp │ ├── iana.hpp │ ├── message.hpp │ ├── message │ │ ├── pack.hpp │ │ ├── types.hpp │ │ └── unpack.hpp │ ├── oemopenbmc.hpp │ ├── oemrouter.hpp │ ├── sessiondef.hpp │ ├── sessionhelper.hpp │ ├── types.hpp │ ├── utility.hpp │ └── utils.hpp └── meson.build ├── ipmi_fru_info_area.cpp ├── ipmi_fru_info_area.hpp ├── ipmiallowlist.hpp ├── ipmid-new.cpp ├── ipmisensor.cpp ├── libipmid ├── entity_map_json.cpp ├── meson.build ├── sdbus-asio.cpp ├── signals.cpp ├── systemintf-sdbus.cpp └── utils.cpp ├── meson.build ├── meson.options ├── oem ├── example │ ├── apphandler.cpp │ └── meson.build └── meson.build ├── read_fru_data.cpp ├── read_fru_data.hpp ├── sbmrhandler.cpp ├── scripts ├── entity-example.md ├── fru-read-example.yaml ├── fru_gen.py ├── inventory-sensor-example.yaml ├── inventory-sensor.py ├── inventorysensor.cpp.mako ├── meson.build ├── readfru.cpp.mako ├── sensor-example.yaml ├── sensor_gen.py └── writesensor.cpp.mako ├── selutility.cpp ├── selutility.hpp ├── sensordatahandler.cpp ├── sensordatahandler.hpp ├── sensorhandler.cpp ├── sensorhandler.hpp ├── settings.cpp ├── settings.hpp ├── softoff ├── mainapp.cpp ├── meson.build ├── softoff.cpp └── softoff.hpp ├── storageaddsel.cpp ├── storageaddsel.hpp ├── storagehandler.cpp ├── subprojects ├── boost.wrap ├── nlohmann_json.wrap ├── phosphor-dbus-interfaces.wrap ├── phosphor-logging.wrap ├── sdbusplus.wrap └── sdeventplus.wrap ├── sys_info_param.cpp ├── sys_info_param.hpp ├── systemintfcmds.cpp ├── systemintfcmds.hpp ├── test ├── dbus-sdr │ └── sensorcommands_unittest.cpp ├── entitymap_json_unittest.cpp ├── meson.build ├── message │ ├── pack.cpp │ ├── payload.cpp │ └── unpack.cpp ├── oemrouter_unittest.cpp └── session │ └── closesession_unittest.cpp ├── transport ├── meson.build └── serialbridge │ ├── meson.build │ ├── serialbridge@.service.in │ ├── serialbridged.cpp │ ├── serialcmd.cpp │ ├── serialcmd.hpp │ └── test │ ├── meson.build │ └── serial_unittest.cpp ├── transportconstants.hpp ├── transporthandler.cpp ├── transporthandler.hpp ├── user_channel ├── channel_layer.cpp ├── channel_layer.hpp ├── channel_mgmt.cpp ├── channel_mgmt.hpp ├── channelcommands.cpp ├── cipher_mgmt.cpp ├── cipher_mgmt.hpp ├── file.hpp ├── meson.build ├── passwd_mgr.cpp ├── passwd_mgr.hpp ├── shadowlock.hpp ├── user_layer.cpp ├── user_layer.hpp ├── user_mgmt.cpp ├── user_mgmt.hpp ├── usercommands.cpp └── usercommands.hpp ├── whitelist-filter.cpp └── xyz └── openbmc_project └── Ipmi └── Internal ├── SoftPowerOff.interface.yaml └── SoftPowerOff └── meson.build /.build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | Dockerfile=$(cat << EOF 4 | FROM ubuntu:15.10 5 | RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get upgrade -yy 6 | RUN DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -yy make g++ gcc libsystemd-dev libc6-dev pkg-config 7 | RUN groupadd -g ${GROUPS[0]} ${USER} && useradd -d ${HOME} -m -u ${UID} -g ${GROUPS[0]} ${USER} 8 | USER ${USER} 9 | ENV HOME ${HOME} 10 | RUN /bin/bash 11 | EOF 12 | ) 13 | 14 | docker pull ubuntu:15.10 15 | docker build -t temp - <<< "${Dockerfile}" 16 | 17 | gcc --version 18 | 19 | docker run --cap-add=sys_admin --net=host --rm=true --user="${USER}" \ 20 | -w "${PWD}" -v "${HOME}":"${HOME}" -t temp make 21 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: Align 10 | AlignTrailingComments: 11 | Kind: Always 12 | OverEmptyLines: 1 13 | AllowAllParametersOfDeclarationOnNextLine: true 14 | AllowShortBlocksOnASingleLine: Empty 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: Empty 17 | AllowShortIfStatementsOnASingleLine: Never 18 | AllowShortLambdasOnASingleLine: true 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakBeforeMultilineStrings: false 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BitFieldColonSpacing: None 24 | BraceWrapping: 25 | AfterCaseLabel: true 26 | AfterClass: true 27 | AfterControlStatement: true 28 | AfterEnum: true 29 | AfterExternBlock: true 30 | AfterFunction: true 31 | AfterNamespace: true 32 | AfterObjCDeclaration: true 33 | AfterStruct: true 34 | AfterUnion: true 35 | BeforeCatch: true 36 | BeforeElse: true 37 | BeforeLambdaBody: false 38 | BeforeWhile: false 39 | IndentBraces: false 40 | SplitEmptyFunction: false 41 | SplitEmptyRecord: false 42 | SplitEmptyNamespace: false 43 | BreakAfterAttributes: Never 44 | BreakAfterReturnType: Automatic 45 | BreakBeforeBinaryOperators: None 46 | BreakBeforeBraces: Custom 47 | BreakBeforeTernaryOperators: true 48 | BreakConstructorInitializers: AfterColon 49 | BreakInheritanceList: AfterColon 50 | BreakStringLiterals: false 51 | BreakTemplateDeclarations: Yes 52 | ColumnLimit: 80 53 | CommentPragmas: '^ IWYU pragma:' 54 | CompactNamespaces: false 55 | ConstructorInitializerIndentWidth: 4 56 | ContinuationIndentWidth: 4 57 | Cpp11BracedListStyle: true 58 | DerivePointerAlignment: false 59 | DisableFormat: false 60 | FixNamespaceComments: true 61 | ForEachMacros: 62 | - foreach 63 | - Q_FOREACH 64 | - BOOST_FOREACH 65 | IncludeBlocks: Regroup 66 | IncludeCategories: 67 | - Regex: '^[<"](gtest|gmock)' 68 | Priority: 7 69 | - Regex: '^"config.h"' 70 | Priority: -1 71 | - Regex: '^".*\.h"' 72 | Priority: 1 73 | - Regex: '^".*\.hpp"' 74 | Priority: 2 75 | - Regex: '^<.*\.h>' 76 | Priority: 3 77 | - Regex: '^<.*\.hpp>' 78 | Priority: 4 79 | - Regex: '^<.*' 80 | Priority: 5 81 | - Regex: '.*' 82 | Priority: 6 83 | IndentCaseLabels: true 84 | IndentExternBlock: NoIndent 85 | IndentRequiresClause: true 86 | IndentWidth: 4 87 | IndentWrappedFunctionNames: true 88 | InsertNewlineAtEOF: true 89 | KeepEmptyLinesAtTheStartOfBlocks: false 90 | LambdaBodyIndentation: Signature 91 | LineEnding: LF 92 | MacroBlockBegin: '' 93 | MacroBlockEnd: '' 94 | MaxEmptyLinesToKeep: 1 95 | NamespaceIndentation: None 96 | ObjCBlockIndentWidth: 2 97 | ObjCSpaceAfterProperty: false 98 | ObjCSpaceBeforeProtocolList: true 99 | PackConstructorInitializers: BinPack 100 | PenaltyBreakAssignment: 25 101 | PenaltyBreakBeforeFirstCallParameter: 50 102 | PenaltyBreakComment: 300 103 | PenaltyBreakFirstLessLess: 120 104 | PenaltyBreakString: 1000 105 | PenaltyBreakTemplateDeclaration: 10 106 | PenaltyExcessCharacter: 1000000 107 | PenaltyReturnTypeOnItsOwnLine: 150 108 | PenaltyIndentedWhitespace: 1 109 | PointerAlignment: Left 110 | QualifierAlignment: Left 111 | ReferenceAlignment: Left 112 | ReflowComments: true 113 | RequiresClausePosition: OwnLine 114 | RequiresExpressionIndentation: Keyword 115 | SortIncludes: CaseSensitive 116 | SortUsingDeclarations: true 117 | SpaceAfterCStyleCast: false 118 | SpaceAfterTemplateKeyword: true 119 | SpaceBeforeAssignmentOperators: true 120 | SpaceBeforeCpp11BracedList: false 121 | SpaceBeforeCtorInitializerColon: true 122 | SpaceBeforeInheritanceColon: true 123 | SpaceBeforeParens: ControlStatements 124 | SpaceBeforeRangeBasedForLoopColon: true 125 | SpaceInEmptyParentheses: false 126 | SpacesBeforeTrailingComments: 1 127 | SpacesInAngles: Never 128 | SpacesInContainerLiterals: true 129 | SpacesInCStyleCastParentheses: false 130 | SpacesInParentheses: false 131 | SpacesInSquareBrackets: false 132 | Standard: Latest 133 | TabWidth: 4 134 | UseTab: Never 135 | ... 136 | 137 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: '-*, 2 | modernize-use-nullptr 3 | ' 4 | 5 | CheckOptions: [] 6 | 7 | HeaderFilterRegex: (?!^subprojects).* 8 | 9 | WarningsAsErrors: '*' 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore vim swap files 2 | .*.sw* 3 | # failures from patch 4 | *.orig 5 | *.rej 6 | # backup files from some editors 7 | *~ 8 | .cscope/ 9 | 10 | # Meson 11 | /build*/ 12 | /subprojects/* 13 | !/subprojects/*.wrap 14 | subprojects/CLI11.wrap 15 | subprojects/function2.wrap 16 | subprojects/googletest.wrap 17 | subprojects/stdplus.wrap 18 | -------------------------------------------------------------------------------- /.shellcheck: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openbmc/phosphor-host-ipmid/d0b99b1137ee811ca1c4a2d6f24f7caab8f2f4c0/.shellcheck -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | sudo: required 4 | 5 | services: 6 | - docker 7 | 8 | script: 9 | - ./.build.sh 10 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | # OWNERS 2 | # ------ 3 | # 4 | # The OWNERS file maintains the list of individuals responsible for various 5 | # parts of this repository, including code review and approval. We use the 6 | # Gerrit 'owners' plugin, which consumes this file, along with some extra 7 | # keywords for our own purposes and tooling. 8 | # 9 | # For details on the configuration used by 'owners' see: 10 | # https://gerrit.googlesource.com/plugins/owners/+/refs/heads/master/owners/src/main/resources/Documentation/config.md 11 | # 12 | # An OWNERS file must be in the root of a repository but may also be present 13 | # in any subdirectory. The contents of the subdirectory OWNERS file are 14 | # combined with parent directories unless 'inherit: false' is set. 15 | # 16 | # The owners file is YAML and has [up to] 4 top-level keywords. 17 | # * owners: A list of individuals who have approval authority on the 18 | # repository. 19 | # 20 | # * reviewers: A list of individuals who have requested review notification 21 | # on the repository. 22 | # 23 | # * matchers: A list of specific file/path matchers for granular 'owners' and 24 | # 'reviewers'. See 'owners' plugin documentation. 25 | # 26 | # * openbmc: A list of openbmc-specific meta-data about owners and reviewers. 27 | # - name: preferred name of the individual. 28 | # - email: preferred email address of the individual. 29 | # - discord: Discord nickname of the individual. 30 | # 31 | # It is expected that these 4 sections will be listed in the order above and 32 | # data within them will be kept sorted. 33 | 34 | owners: 35 | - liuxiwei@ieisystem.com 36 | - rushtotom@gmail.com 37 | - vernon.mauery@gmail.com 38 | 39 | reviewers: 40 | - anoo@us.ibm.com 41 | - deepak.kodihalli.83@gmail.com 42 | - ratankgupta31@gmail.com 43 | 44 | matchers: 45 | - partial_regex: oem/example 46 | owners: 47 | - vernon.mauery@gmail.com 48 | 49 | openbmc: 50 | - name: Adriana Kobylak 51 | email: anoo@us.ibm.com 52 | discord: anoo 53 | - name: Deepak Kodihalli 54 | email: deepak.kodihalli.83@gmail.com 55 | discord: dkodihal 56 | - name: George Liu 57 | email: liuxiwei@ieisystem.com 58 | discord: George Liu 59 | - name: Ratan Gupta 60 | email: ratankgupta31@gmail.com 61 | discord: rgupta 62 | - name: Tom Joseph 63 | email: rushtotom@gmail.com 64 | discord: tomjose 65 | - name: Vernon Mauery 66 | email: vernon.mauery@gmail.com 67 | discord: vmauery 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # phosphor-host-ipmid 2 | 3 | ## Compile ipmid with default options 4 | 5 | ```ascii 6 | meson builddir 7 | ninja -C builddir 8 | ``` 9 | 10 | ## Compile ipmid with yocto defaults 11 | 12 | ```ascii 13 | meson builddir -Dbuildtype=minsize -Db_lto=true -Dtests=disabled 14 | ninja -C builddir 15 | ``` 16 | 17 | If any of the dependencies are not found on the host system during 18 | configuration, meson automatically gets them via its wrap dependencies mentioned 19 | in `ipmid/subprojects`. 20 | 21 | ## Enable/Disable meson wrap feature 22 | 23 | ```ascii 24 | meson builddir -Dwrap_mode=nofallback 25 | ninja -C builddir 26 | ``` 27 | 28 | ## Enable debug traces 29 | 30 | ```ascii 31 | meson builddir -Dbuildtype=debug 32 | ninja -C builddir 33 | ``` 34 | 35 | ## Generate test coverage report 36 | 37 | ```ascii 38 | meson builddir -Db_coverage=true -Dtests=enabled 39 | ninja -C builddir test 40 | ninja -C builddir coverage 41 | ``` 42 | -------------------------------------------------------------------------------- /app/channel.hpp: -------------------------------------------------------------------------------- 1 | #include "nlohmann/json.hpp" 2 | 3 | #include 4 | 5 | /** @brief this command is used to look up what authentication, integrity, 6 | * confidentiality algorithms are supported. 7 | * 8 | * @ param ctx - context pointer 9 | * @ param channelNumber - channel number 10 | * @ param payloadType - payload type 11 | * @ param listIndex - list index 12 | * @ param algoSelectBit - list algorithms 13 | * 14 | * @returns ipmi completion code plus response data 15 | * - rspChannel - channel number for authentication algorithm. 16 | * - rspRecords - cipher suite records. 17 | **/ 18 | ipmi::RspType // Cipher Records 20 | > 21 | getChannelCipherSuites(ipmi::Context::ptr ctx, uint4_t channelNumber, 22 | uint4_t reserved1, uint8_t payloadType, 23 | uint6_t listIndex, uint1_t reserved2, 24 | uint1_t algoSelectBit); 25 | 26 | namespace cipher 27 | { 28 | 29 | static constexpr auto listCipherSuite = 0x80; 30 | 31 | using Json = nlohmann::json; 32 | static constexpr auto configFile = "/usr/share/ipmi-providers/cipher_list.json"; 33 | static constexpr auto cipher = "cipher"; 34 | static constexpr auto stdCipherSuite = 0xC0; 35 | static constexpr auto oemCipherSuite = 0xC1; 36 | static constexpr auto oem = "oemiana"; 37 | static constexpr auto auth = "authentication"; 38 | static constexpr auto integrity = "integrity"; 39 | static constexpr auto integrityTag = 0x40; 40 | static constexpr auto conf = "confidentiality"; 41 | static constexpr auto confTag = 0x80; 42 | 43 | } // namespace cipher 44 | -------------------------------------------------------------------------------- /app/meson.build: -------------------------------------------------------------------------------- 1 | app_inc = include_directories('.') 2 | 3 | app_pre = declare_dependency(include_directories: [root_inc, app_inc]) 4 | 5 | app_lib = static_library( 6 | 'app', 7 | 'channel.cpp', 8 | 'watchdog.cpp', 9 | 'watchdog_service.cpp', 10 | implicit_include_directories: false, 11 | dependencies: app_pre, 12 | ) 13 | 14 | app_dep = declare_dependency(link_with: app_lib, dependencies: app_pre) 15 | -------------------------------------------------------------------------------- /app/watchdog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** @brief The RESET watchdog IPMI command. 6 | */ 7 | ipmi::RspType<> ipmiAppResetWatchdogTimer(); 8 | 9 | /**@brief The setWatchdogTimer ipmi command. 10 | * 11 | * @param 12 | * - timerUse 13 | * - dontStopTimer 14 | * - dontLog 15 | * - timerAction 16 | * - pretimeout 17 | * - expireFlags 18 | * - initialCountdown 19 | * 20 | * @return completion code on success. 21 | **/ 22 | ipmi::RspType<> ipmiSetWatchdogTimer( 23 | uint3_t timerUse, uint3_t reserved, bool dontStopTimer, bool dontLog, 24 | uint3_t timeoutAction, uint1_t reserved1, uint3_t preTimeoutInterrupt, 25 | uint1_t reserved2, uint8_t preTimeoutInterval, std::bitset<8> expFlagValue, 26 | uint16_t initialCountdown); 27 | 28 | /**@brief The getWatchdogTimer ipmi command. 29 | * 30 | * @return 31 | * - timerUse 32 | * - timerActions 33 | * - pretimeout 34 | * - timeruseFlags 35 | * - initialCountdown 36 | * - presentCountdown 37 | **/ 38 | ipmi::RspType, // expireFlags 42 | uint16_t, // initial Countdown - Little Endian (deciseconds) 43 | uint16_t // present Countdown - Little Endian (deciseconds) 44 | > 45 | ipmiGetWatchdogTimer(); 46 | -------------------------------------------------------------------------------- /app/watchdog_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | /** @class WatchdogService 7 | * @brief Access to the running OpenBMC watchdog implementation. 8 | * @details Easy accessor for servers that implement the 9 | * xyz.openbmc_project.State.Watchdog DBus API. 10 | */ 11 | class WatchdogService 12 | { 13 | public: 14 | WatchdogService(); 15 | 16 | using Action = 17 | sdbusplus::server::xyz::openbmc_project::state::Watchdog::Action; 18 | using TimerUse = 19 | sdbusplus::server::xyz::openbmc_project::state::Watchdog::TimerUse; 20 | 21 | /** @brief Resets the time remaining on the watchdog. 22 | * Equivalent to setTimeRemaining(getInterval()). 23 | * Optionally enables the watchdog. 24 | * 25 | * @param[in] enableWatchdog - Should the call also enable the watchdog 26 | */ 27 | void resetTimeRemaining(bool enableWatchdog); 28 | 29 | /** @brief Contains a copy of the properties enumerated by the 30 | * watchdog service. 31 | */ 32 | struct Properties 33 | { 34 | bool initialized; 35 | bool enabled; 36 | Action expireAction; 37 | TimerUse timerUse; 38 | TimerUse expiredTimerUse; 39 | uint64_t interval; 40 | uint64_t timeRemaining; 41 | }; 42 | 43 | /** @brief Retrieves a copy of the currently set properties on the 44 | * host watchdog 45 | * 46 | * @return A populated WatchdogProperties struct 47 | */ 48 | Properties getProperties(); 49 | 50 | /** @brief Get the value of the initialized property on the host 51 | * watchdog 52 | * 53 | * @return The value of the property 54 | */ 55 | bool getInitialized(); 56 | 57 | /** @brief Sets the value of the initialized property on the host 58 | * watchdog 59 | * 60 | * @param[in] initialized - The new initializedvalue 61 | */ 62 | void setInitialized(bool initialized); 63 | 64 | /** @brief Sets the value of the enabled property on the host watchdog 65 | * 66 | * @param[in] enabled - The new enabled value 67 | */ 68 | void setEnabled(bool enabled); 69 | 70 | /** @brief Sets the value of the LogTimeout property on the host watchdog 71 | * 72 | * @param[in] LogTimeout - The new LogTimeout value 73 | */ 74 | void setLogTimeout(bool LogTimeout); 75 | 76 | /** @brief Sets the value of the expireAction property on the host watchdog 77 | * 78 | * @param[in] expireAction - The new expireAction value 79 | */ 80 | void setExpireAction(Action expireAction); 81 | 82 | /** @brief Sets the value of the timerUse property on the host watchdog 83 | * 84 | * @param[in] timerUse - The new timerUse value 85 | */ 86 | void setTimerUse(TimerUse timerUse); 87 | 88 | /** @brief Sets the value of the ExpiredTimerUse property on the host 89 | * watchdog 90 | * 91 | * @param[in] timerUse - The new timerUse value 92 | */ 93 | void setExpiredTimerUse(TimerUse timerUse); 94 | 95 | /** @brief Sets the value of the interval property on the host watchdog 96 | * 97 | * @param[in] interval - The new interval value 98 | */ 99 | void setInterval(uint64_t interval); 100 | 101 | private: 102 | /** @brief sdbusplus handle */ 103 | sdbusplus::bus_t bus; 104 | /** @brief The name of the mapped host watchdog service */ 105 | static ipmi::ServiceCache wd_service; 106 | 107 | /** @brief Gets the value of the property on the host watchdog 108 | * 109 | * @param[in] key - The name of the property 110 | * @return The value of the property 111 | */ 112 | template 113 | T getProperty(const std::string& key); 114 | 115 | /** @brief Sets the value of the property on the host watchdog 116 | * 117 | * @param[in] key - The name of the property 118 | * @param[in] val - The new value 119 | */ 120 | template 121 | void setProperty(const std::string& key, const T& val); 122 | }; 123 | -------------------------------------------------------------------------------- /apphandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum ipmi_app_sysinfo_params 6 | { 7 | IPMI_SYSINFO_SET_STATE = 0x00, 8 | IPMI_SYSINFO_SYSTEM_FW_VERSION = 0x01, 9 | IPMI_SYSINFO_SYSTEM_NAME = 0x02, 10 | IPMI_SYSINFO_PRIMARY_OS_NAME = 0x03, 11 | IPMI_SYSINFO_OS_NAME = 0x04, 12 | IPMI_SYSINFO_OS_VERSION = 0x05, 13 | IPMI_SYSINFO_BMC_URL = 0x06, 14 | IPMI_SYSINFO_OS_HYP_URL = 0x07, 15 | IPMI_SYSINFO_OEM_START = 0xC0, // Start of range of OEM parameters 16 | }; 17 | -------------------------------------------------------------------------------- /chassishandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | // Command specific completion codes 8 | enum ipmi_chassis_return_codes 9 | { 10 | IPMI_OK = 0x0, 11 | IPMI_CC_PARM_NOT_SUPPORTED = 0x80, 12 | IPMI_CC_FAIL_SET_IN_PROGRESS = 0x81, 13 | }; 14 | 15 | // Generic completion codes, 16 | // see IPMI doc section 5.2 17 | enum ipmi_generic_return_codes 18 | { 19 | IPMI_OUT_OF_SPACE = 0xC4, 20 | }; 21 | 22 | // Various Chassis operations under a single command. 23 | enum ipmi_chassis_control_cmds : uint8_t 24 | { 25 | CMD_POWER_OFF = 0x00, 26 | CMD_POWER_ON = 0x01, 27 | CMD_POWER_CYCLE = 0x02, 28 | CMD_HARD_RESET = 0x03, 29 | CMD_PULSE_DIAGNOSTIC_INTR = 0x04, 30 | CMD_SOFT_OFF_VIA_OVER_TEMP = 0x05, 31 | }; 32 | enum class BootOptionParameter : size_t 33 | { 34 | setInProgress = 0x0, 35 | bootFlagValidClr = 0x3, 36 | bootInfo = 0x4, 37 | bootFlags = 0x5, 38 | opalNetworkSettings = 0x61 39 | }; 40 | 41 | enum class BootOptionResponseSize : size_t 42 | { 43 | setInProgress = 3, 44 | bootFlags = 5, 45 | opalNetworkSettings = 50 46 | }; 47 | 48 | enum class ChassisIDState : uint8_t 49 | { 50 | off = 0x0, 51 | temporaryOn = 0x1, 52 | indefiniteOn = 0x2, 53 | reserved = 0x3 54 | }; 55 | -------------------------------------------------------------------------------- /dbus-sdr/meson.build: -------------------------------------------------------------------------------- 1 | sensorutils_lib = static_library( 2 | 'sensorutils', 3 | 'sensorutils.cpp', 4 | include_directories: root_inc, 5 | implicit_include_directories: false, 6 | ) 7 | 8 | sensorutils_dep = declare_dependency(link_with: sensorutils_lib) 9 | 10 | hybrid_src = [] 11 | 12 | if get_option('hybrid-sensors').allowed() 13 | hybrid_src = [ 14 | 'sensorhandler.cpp', 15 | 'sensordatahandler.cpp', 16 | 'ipmisensor.cpp', 17 | generated_src, 18 | ] 19 | endif 20 | 21 | sensorsoem_src = [] 22 | if get_option('sensors-oem').allowed() 23 | sensorsoem_src = ['dbus-sdr/sensorcommands_oem.cpp'] 24 | endif 25 | 26 | dbus_sdr_pre = declare_dependency( 27 | include_directories: root_inc, 28 | dependencies: [ 29 | crypto, 30 | nlohmann_json_dep, 31 | phosphor_logging_dep, 32 | ipmid_dep, 33 | sensorutils_dep, 34 | ], 35 | ) 36 | 37 | dbus_sdr_src = [ 38 | 'dbus-sdr/sdrutils.cpp', 39 | 'dbus-sdr/sensorcommands.cpp', 40 | 'dbus-sdr/storagecommands.cpp', 41 | hybrid_src, 42 | sensorsoem_src, 43 | ] 44 | -------------------------------------------------------------------------------- /dcmihandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nlohmann/json.hpp" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace dcmi 12 | { 13 | 14 | static constexpr auto propIntf = "org.freedesktop.DBus.Properties"; 15 | static constexpr auto assetTagIntf = 16 | "xyz.openbmc_project.Inventory.Decorator.AssetTag"; 17 | static constexpr auto assetTagProp = "AssetTag"; 18 | static constexpr auto networkServiceName = "xyz.openbmc_project.Network"; 19 | static constexpr auto networkConfigObj = "/xyz/openbmc_project/network/config"; 20 | static constexpr auto networkConfigIntf = 21 | "xyz.openbmc_project.Network.SystemConfiguration"; 22 | static constexpr auto hostNameProp = "HostName"; 23 | static constexpr auto temperatureSensorType = 0x01; 24 | static constexpr size_t maxInstances = 255; 25 | static constexpr uint8_t maxRecords = 8; 26 | static constexpr auto gDCMISensorsConfig = 27 | "/usr/share/ipmi-providers/dcmi_sensors.json"; 28 | static constexpr auto ethernetIntf = 29 | "xyz.openbmc_project.Network.EthernetInterface"; 30 | static constexpr auto ethernetDefaultChannelNum = 0x1; 31 | static constexpr auto networkRoot = "/xyz/openbmc_project/network"; 32 | static constexpr auto dhcpIntf = 33 | "xyz.openbmc_project.Network.DHCPConfiguration"; 34 | static constexpr auto systemBusName = "org.freedesktop.systemd1"; 35 | static constexpr auto systemPath = "/org/freedesktop/systemd1"; 36 | static constexpr auto systemIntf = "org.freedesktop.systemd1.Manager"; 37 | static constexpr auto gDCMICapabilitiesConfig = 38 | "/usr/share/ipmi-providers/dcmi_cap.json"; 39 | static constexpr auto gDCMIPowerMgmtCapability = "PowerManagement"; 40 | static constexpr auto gDCMIPowerMgmtSupported = 0x1; 41 | static constexpr auto gMaxSELEntriesMask = 0xFFF; 42 | static constexpr auto gByteBitSize = 8; 43 | 44 | /** @brief Check whether DCMI power management is supported 45 | * in the DCMI Capabilities config file. 46 | * 47 | * @return True if DCMI power management is supported 48 | */ 49 | bool isDCMIPowerMgmtSupported(); 50 | 51 | } // namespace dcmi 52 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | ## Device ID Configuration 4 | 5 | There is a default dev_id.json file provided by 6 | meta-phosphor/common/recipes-phosphor/ipmi/phosphor-ipmi-host.bb 7 | 8 | Any target can override the default json file by providing a 9 | phosphor-ipmi-host.bbappend with an ODM or platform customizable configuration. 10 | 11 | For a specific example, see: 12 | [Witherspoon](https://github.com/openbmc/openbmc/blob/master/meta-openbmc-machines/meta-openpower/meta-ibm/meta-witherspoon/recipes-phosphor/ipmi/phosphor-ipmi-host.bbappend) 13 | 14 | The JSON format for get_device_id: 15 | 16 | {"id": 0, "revision": 0, "addn_dev_support": 0, 17 | "manuf_id": 0, "prod_id": 0, "aux": 0} 18 | 19 | Each value in this JSON object should be an integer. The file is placed in 20 | /usr/share/ipmi-providers/ by Yocto, and will be parsed upon the first call to 21 | get_device_id. The data is then cached for future use. If you change the data at 22 | runtime, simply restart the service to see the new data fetched by a call to 23 | get_device_id. 24 | 25 | ## IPMI D-Bus Sensor Filtering 26 | 27 | Phosphor-ipmi-host provides a compile time option to control how IPMI sensors 28 | are populated. The default model is for the sensors to be culled from a set of 29 | JSON files. There is another model that collects the sensors to display from 30 | D-Bus. The D-Bus method is the default mode used by Redfish. Redfish does not 31 | have a fixed limit on the number of sensors that can be reported. 32 | 33 | IPMI supports a smaller number of sensors than are available via Redfish. The 34 | limit being the number of Sensor Data Records (SDR) supported in the IPMI 35 | specification. Enabling IPMI to use D-Bus may cause the number of sensors 36 | retrieved to exceed the number SDRs IPMI can support. Even if the number of 37 | sensors retrieved is within the SDR limit IPMI can support, it may be desirable 38 | to filter entries that are uninteresting. 39 | 40 | Meson uses the _dyanmic-sensors_ configuration option to enable retrieving the 41 | sensors via D-Bus. When dynamic sensors are active all of the sensors placed on 42 | D-Bus by a service are added to the IPMI sensor list. In the event that too many 43 | sensors are exposed on D-Bus, the list can be filtered by adding a list of 44 | services to filter to _/usr/share/ipmi-providers/sensor_filter.json_. 45 | 46 | Example filtering: 47 | 48 | ```json 49 | { 50 | "ServiceFilter": [ 51 | "xyz.openbmc_project.RedfishSensors1", 52 | "xyz.openbmc_project.RedfishSensors2" 53 | ] 54 | } 55 | ``` 56 | 57 | Any sensors published by the example services are blocked from being added to 58 | the IPMI SDR table. 59 | -------------------------------------------------------------------------------- /docs/ipmi-network-format.md: -------------------------------------------------------------------------------- 1 | # IPMI network format 2 | 3 | On platforms running AMI BMC firmware (Habanero and Firestone at the moment), 4 | the 'Get/Set' System Boot Options' command has been extended to allow overrides 5 | to the network configuration to be specified. In Petitboot this will cause any 6 | existing network configuration to be overridden by the specified configuration. 7 | The format of each IPMI request is similar to that of the usual boot options 8 | request, with a slightly different payload. 9 | 10 | The start of the request is similar to a usual boot option override, but 11 | specifies the fields of interest differently, ie; 12 | 13 | Specify 'chassis bootdev', field 96, data1 0x00 0x08 0x61 0x80 14 | 15 | The rest of request format is defined by Petitboot as: - 4 byte cookie value 16 | (always 0x21 0x70 0x62 0x21) - 2 byte version value (always 0x00 0x01) - 1 byte 17 | hardware address size (eg. 0x06 for MAC address) - 1 byte IP address size (eg. 18 | 0x04 for IPv4) - Hardware (MAC) address - 1 byte flags for 'ignore' and 19 | 'method', where method = 0 is DHCP and method = 1 is Static. And for static 20 | configs: - IP Address - 1 byte subnet value - Gateway address 21 | 22 | Describing each field in more detail: 23 | 24 | Specify 'chassis bootdev', field 96, data1 0x00 0x08 0x61 0x80 25 | 26 | Set a special cookie that Petitboot will recognise: 0x21 0x70 0x62 0x21 27 | 28 | Specify the version (only 1 at the moment) 0x00 0x01 29 | 30 | Specify the size of the MAC address and IP address. This is used to 31 | differentiate between IPv4 and IPv6 addresses, or potential future support for 32 | Infiniband/etc. 0x06 0x04 (6-byte MAC address, IPv4 IP address) 33 | 34 | Set the hardware address of the interface you want to override, eg: 0xf4 0x52 35 | 0x14 0xf3 0x01 0xdf 36 | 37 | Specify 'ignore' or 'static/dynamic' flags. The second byte specifies to use 38 | either DHCP or static IP configuration (0 for DHCP). 0x00 0x01 39 | 40 | The below fields are required if setting a static configuration: 41 | 42 | Set the IP address you want to use, eg: 0x0a 0x3d 0xa1 0x42 43 | 44 | Set the subnet mask (short notation), eg '16': 0x10 45 | 46 | Set the gateway address, eg: 0x0a 0x3d 0x2 0x1 47 | 48 | All together this should look like: 0x00 0x08 0x61 0x80 0x21 0x70 0x62 0x21 0x00 49 | 0x01 0x06 0x04 0xf4 0x52 0x14 0xf3 0x01 0xdf 0x00 0x01 0x0a 0x3d 0xa1 0x42 0x10 50 | 0x0a 0x3d 0x2 0x1 51 | 52 | To clear a network override, it is sufficient to clear out the request, or set a 53 | zero-cookie which Petitboot will reject. Eg: 0x00 0x08 0x61 0x80 0x00 0x00 0x00 54 | 0x00 55 | 56 | You can 'Get' the override back with 0x00 0x09 0x61 0x80 0x00 which should 57 | return whatever is currently set. 58 | -------------------------------------------------------------------------------- /docs/ipmitool-commands-cheatsheet.md: -------------------------------------------------------------------------------- 1 | # IPMI command cheat sheet 2 | 3 | This document is intended to provide a set of IPMI commands for quick reference. 4 | 5 | Note: If the ipmitool is on the BMC then set the interface as "-I dbus" and if 6 | the ipmitool is outside the BMC (i.e on the network) then set the interface as 7 | "-I lanplus". 8 | 9 | ## Network Configuration 10 | 11 | ### Set the interface mode 12 | 13 | `ipmitool lan set ipsrc static` 14 | 15 | ### Set the IP Address 16 | 17 | `ipmitool lan set ipaddr ` 18 | 19 | ### Set the network mask 20 | 21 | `ipmitool lan set netmask ` 22 | 23 | ### Set the default gateway 24 | 25 | `ipmitool lan set defgw ipaddr ` 26 | 27 | ### Set the VLAN 28 | 29 | `ipmitool lan set vlan id ` 30 | 31 | ### Delete the VLAN 32 | 33 | `ipmitool lan set vlan id off` 34 | 35 | NOTE: The user can group multiple set operations since the IPMI daemon waits for 36 | 10 seconds after each set operation before applying the configuration. 37 | -------------------------------------------------------------------------------- /error-HostEvent.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace sdbusplus::error::org::open_power::host 8 | { 9 | struct Event final : public sdbusplus::exception::generated_exception 10 | { 11 | static constexpr auto errName = "org.open_power.Host.Error.Event"; 12 | static constexpr auto errDesc = "A host system event was received"; 13 | static constexpr auto errWhat = 14 | "org.open_power.Host.Error.Event: A host system event was received"; 15 | 16 | const char* name() const noexcept override 17 | { 18 | return errName; 19 | } 20 | const char* description() const noexcept override 21 | { 22 | return errDesc; 23 | } 24 | const char* what() const noexcept override 25 | { 26 | return errWhat; 27 | } 28 | }; 29 | struct MaintenanceProcedure final : 30 | public sdbusplus::exception::generated_exception 31 | { 32 | static constexpr auto errName = 33 | "org.open_power.Host.Error.MaintenanceProcedure"; 34 | static constexpr auto errDesc = 35 | "A host system event with a procedure callout"; 36 | static constexpr auto errWhat = 37 | "org.open_power.Host.Error.MaintenanceProcedure: A host system event with a procedure callout"; 38 | 39 | const char* name() const noexcept override 40 | { 41 | return errName; 42 | } 43 | const char* description() const noexcept override 44 | { 45 | return errDesc; 46 | } 47 | const char* what() const noexcept override 48 | { 49 | return errWhat; 50 | } 51 | }; 52 | } // namespace sdbusplus::error::org::open_power::host 53 | 54 | #ifndef SDBUSPP_REMOVE_DEPRECATED_NAMESPACE 55 | namespace sdbusplus::org::open_power::Host::Error 56 | { 57 | using Event = sdbusplus::error::org::open_power::host::Event; 58 | using MaintenanceProcedure = 59 | sdbusplus::error::org::open_power::host::MaintenanceProcedure; 60 | } // namespace sdbusplus::org::open_power::Host::Error 61 | #endif 62 | -------------------------------------------------------------------------------- /fruread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct IPMIFruData 11 | { 12 | std::string section; 13 | std::string property; 14 | std::string delimiter; 15 | }; 16 | 17 | using DbusProperty = std::string; 18 | using DbusPropertyVec = std::vector>; 19 | 20 | using DbusInterface = std::string; 21 | using DbusInterfaceVec = std::vector>; 22 | 23 | using FruInstancePath = std::string; 24 | 25 | struct FruInstance 26 | { 27 | uint8_t entityID; 28 | uint8_t entityInstance; 29 | FruInstancePath path; 30 | DbusInterfaceVec interfaces; 31 | }; 32 | 33 | using FruInstanceVec = std::vector; 34 | 35 | using FruId = uint32_t; 36 | using FruMap = std::map; 37 | -------------------------------------------------------------------------------- /generate_whitelist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Ensure some files have been passed. 4 | if [ -z "$*" ]; then 5 | echo "Usage: $0 [allowlist_files+]" >&2 6 | exit 1 7 | fi 8 | 9 | cat << EOF 10 | #include 11 | 12 | const std::vector allowlist = { 13 | 14 | EOF 15 | 16 | # Output each row of allowlist vector. 17 | # Concatenate all the passed files. 18 | # Remove comments and empty lines. 19 | # Sort the list [numerically]. 20 | # Remove any duplicates. 21 | # Turn "a:b //:" -> "{ a, b }, //:" 22 | sed "s/#.*//" "$*" | sed '/^$/d' | sort -n | uniq | sed "s/^/ { /" | \ 23 | sed "s/\:\(....\)\(.*\)/ , \1 }, \2/" 24 | 25 | cat << EOF 26 | }; 27 | EOF 28 | -------------------------------------------------------------------------------- /generate_whitelist_create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Ensure some files have been passed. 4 | ./generate_whitelist.sh "$*" > ipmiwhitelist.cpp 5 | -------------------------------------------------------------------------------- /globalhandler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | static constexpr auto bmcStateRoot = "/xyz/openbmc_project/state"; 9 | static constexpr auto bmcStateIntf = "xyz.openbmc_project.State.BMC"; 10 | static constexpr auto reqTransition = "RequestedBMCTransition"; 11 | static constexpr auto match = "bmc0"; 12 | 13 | using BMC = sdbusplus::server::xyz::openbmc_project::state::BMC; 14 | 15 | void registerNetFnGlobalFunctions() __attribute__((constructor)); 16 | 17 | /** @brief implements cold and warm reset commands 18 | * @param - None 19 | * @returns IPMI completion code. 20 | */ 21 | ipmi::RspType<> ipmiGlobalReset(ipmi::Context::ptr ctx) 22 | { 23 | ipmi::DbusObjectInfo bmcStateObj; 24 | boost::system::error_code ec = ipmi::getDbusObject( 25 | ctx, bmcStateIntf, bmcStateRoot, match, bmcStateObj); 26 | if (!ec) 27 | { 28 | std::string service; 29 | ec = ipmi::getService(ctx, bmcStateIntf, bmcStateObj.first, service); 30 | if (!ec) 31 | { 32 | ec = ipmi::setDbusProperty( 33 | ctx, service, bmcStateObj.first, bmcStateIntf, reqTransition, 34 | convertForMessage(BMC::Transition::Reboot)); 35 | } 36 | } 37 | if (ec) 38 | { 39 | lg2::error("Exception in Global Reset: {ERROR}", "ERROR", ec.message()); 40 | return ipmi::responseUnspecifiedError(); 41 | } 42 | 43 | // Status code. 44 | return ipmi::responseSuccess(); 45 | } 46 | 47 | void registerNetFnGlobalFunctions() 48 | { 49 | // Cold Reset 50 | ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 51 | ipmi::app::cmdColdReset, ipmi::Privilege::Admin, 52 | ipmiGlobalReset); 53 | return; 54 | } 55 | -------------------------------------------------------------------------------- /groupext.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define GRPEXT_GET_GROUP_CMD 0 6 | void registerNetFnGroupExtFunctions() __attribute__((constructor)); 7 | 8 | ipmi_ret_t ipmi_groupext(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, 9 | ipmi_response_t response, ipmi_data_len_t data_len, 10 | ipmi_context_t) 11 | { 12 | // Generic return from IPMI commands. 13 | ipmi_ret_t rc = IPMI_CC_OK; 14 | uint8_t* p = (uint8_t*)response; 15 | 16 | std::printf("IPMI GROUP EXTENSIONS\n"); 17 | 18 | *data_len = 1; 19 | *p = 0; 20 | 21 | return rc; 22 | } 23 | 24 | void registerNetFnGroupExtFunctions() 25 | { 26 | // 27 | ipmi_register_callback(ipmi::netFnGroup, GRPEXT_GET_GROUP_CMD, nullptr, 28 | ipmi_groupext, PRIVILEGE_USER); 29 | 30 | return; 31 | } 32 | -------------------------------------------------------------------------------- /host-cmd-manager.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include "host-cmd-manager.hpp" 4 | 5 | #include "systemintfcmds.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace phosphor 18 | { 19 | namespace host 20 | { 21 | namespace command 22 | { 23 | 24 | constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0"; 25 | constexpr auto HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host"; 26 | constexpr auto HOST_TRANS_PROP = "RequestedHostTransition"; 27 | 28 | // For throwing exceptions 29 | using namespace phosphor::logging; 30 | using InternalFailure = 31 | sdbusplus::error::xyz::openbmc_project::common::InternalFailure; 32 | 33 | namespace sdbusRule = sdbusplus::bus::match::rules; 34 | 35 | Manager::Manager(sdbusplus::bus_t& bus) : 36 | bus(bus), timer(std::bind(&Manager::hostTimeout, this)), 37 | hostTransitionMatch( 38 | bus, 39 | sdbusRule::propertiesChanged(HOST_STATE_PATH, HOST_STATE_INTERFACE), 40 | std::bind(&Manager::clearQueueOnPowerOn, this, std::placeholders::_1)) 41 | { 42 | // Nothing to do here. 43 | } 44 | 45 | // Called as part of READ_MSG_DATA command 46 | IpmiCmdData Manager::getNextCommand() 47 | { 48 | // Stop the timer. Don't have to Err failure doing so. 49 | auto r = timer.stop(); 50 | if (r < 0) 51 | { 52 | lg2::error("Failure to STOP the timer: {ERROR}", "ERROR", strerror(-r)); 53 | } 54 | 55 | if (this->workQueue.empty()) 56 | { 57 | // Just return a heartbeat in this case. A spurious SMS_ATN was 58 | // asserted for the host (probably from a previous boot). 59 | lg2::debug("Control Host work queue is empty!"); 60 | 61 | return std::make_pair(CMD_HEARTBEAT, 0x00); 62 | } 63 | 64 | // Pop the processed entry off the queue 65 | auto command = this->workQueue.front(); 66 | this->workQueue.pop(); 67 | 68 | // IPMI command is the first element in pair 69 | auto ipmiCmdData = std::get<0>(command); 70 | 71 | // Now, call the user registered functions so that 72 | // implementation specific CommandComplete signals 73 | // can be sent. `true` indicating Success. 74 | std::get(command)(ipmiCmdData, true); 75 | 76 | // Check for another entry in the queue and kick it off 77 | this->checkQueueAndAlertHost(); 78 | 79 | // Tuple of command and data 80 | return ipmiCmdData; 81 | } 82 | 83 | // Called when initial timer goes off post sending SMS_ATN 84 | void Manager::hostTimeout() 85 | { 86 | lg2::error("Host control timeout hit!"); 87 | 88 | clearQueue(); 89 | } 90 | 91 | void Manager::clearQueue() 92 | { 93 | // Dequeue all entries and send fail signal 94 | while (!this->workQueue.empty()) 95 | { 96 | auto command = this->workQueue.front(); 97 | this->workQueue.pop(); 98 | 99 | // IPMI command is the first element in pair 100 | auto ipmiCmdData = std::get<0>(command); 101 | 102 | // Call the implementation specific Command Failure. 103 | // `false` indicating Failure 104 | std::get(command)(ipmiCmdData, false); 105 | } 106 | } 107 | 108 | // Called for alerting the host 109 | void Manager::checkQueueAndAlertHost() 110 | { 111 | if (this->workQueue.size() >= 1) 112 | { 113 | lg2::debug("Asserting SMS Attention"); 114 | 115 | std::string HOST_IPMI_SVC("org.openbmc.HostIpmi"); 116 | std::string IPMI_PATH("/org/openbmc/HostIpmi/1"); 117 | std::string IPMI_INTERFACE("org.openbmc.HostIpmi"); 118 | 119 | // Start the timer for this transaction 120 | auto time = std::chrono::duration_cast( 121 | std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS)); 122 | 123 | auto r = timer.start(time); 124 | if (r < 0) 125 | { 126 | lg2::error("Error starting timer for control host"); 127 | return; 128 | } 129 | 130 | auto method = 131 | this->bus.new_method_call(HOST_IPMI_SVC.c_str(), IPMI_PATH.c_str(), 132 | IPMI_INTERFACE.c_str(), "setAttention"); 133 | 134 | try 135 | { 136 | auto reply = this->bus.call(method); 137 | 138 | lg2::debug("SMS Attention asserted"); 139 | } 140 | catch (sdbusplus::exception_t& e) 141 | { 142 | lg2::error("Error when call setAttention method"); 143 | } 144 | } 145 | } 146 | 147 | // Called by specific implementations that provide commands 148 | void Manager::execute(CommandHandler command) 149 | { 150 | lg2::debug("Pushing cmd on to queue, command: {COMMAND}", "COMMAND", 151 | std::get<0>(command).first); 152 | 153 | this->workQueue.emplace(command); 154 | 155 | // Alert host if this is only command in queue otherwise host will 156 | // be notified of next message after processing the current one 157 | if (this->workQueue.size() == 1) 158 | { 159 | this->checkQueueAndAlertHost(); 160 | } 161 | else 162 | { 163 | lg2::info("Command in process, no attention"); 164 | } 165 | 166 | return; 167 | } 168 | 169 | void Manager::clearQueueOnPowerOn(sdbusplus::message_t& msg) 170 | { 171 | namespace server = sdbusplus::server::xyz::openbmc_project::state; 172 | 173 | ::ipmi::DbusInterface interface; 174 | ::ipmi::PropertyMap properties; 175 | 176 | msg.read(interface, properties); 177 | 178 | if (properties.find(HOST_TRANS_PROP) == properties.end()) 179 | { 180 | return; 181 | } 182 | 183 | auto& requestedState = 184 | std::get(properties.at(HOST_TRANS_PROP)); 185 | 186 | if (server::Host::convertTransitionFromString(requestedState) == 187 | server::Host::Transition::On) 188 | { 189 | clearQueue(); 190 | } 191 | } 192 | 193 | } // namespace command 194 | } // namespace host 195 | } // namespace phosphor 196 | -------------------------------------------------------------------------------- /host-cmd-manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace phosphor 12 | { 13 | namespace host 14 | { 15 | namespace command 16 | { 17 | 18 | /** @class 19 | * @brief Manages commands that are to be sent to Host 20 | */ 21 | class Manager 22 | { 23 | public: 24 | Manager() = delete; 25 | ~Manager() = default; 26 | Manager(const Manager&) = delete; 27 | Manager& operator=(const Manager&) = delete; 28 | Manager(Manager&&) = delete; 29 | Manager& operator=(Manager&&) = delete; 30 | 31 | /** @brief Constructs Manager object 32 | * 33 | * @param[in] bus - dbus handler 34 | * @param[in] event - pointer to sd_event 35 | */ 36 | explicit Manager(sdbusplus::bus_t& bus); 37 | 38 | /** @brief Extracts the next entry in the queue and returns 39 | * Command and data part of it. 40 | * 41 | * @detail Also calls into the registered handlers so that they can now 42 | * send the CommandComplete signal since the interface contract 43 | * is that we emit this signal once the message has been 44 | * passed to the host (which is required when calling this) 45 | * 46 | * Also, if the queue has more commands, then it will alert the 47 | * host 48 | */ 49 | IpmiCmdData getNextCommand(); 50 | 51 | /** @brief Pushes the command onto the queue. 52 | * 53 | * @detail If the queue is empty, then it alerts the Host. If not, 54 | * then it returns and the API documented above will handle 55 | * the commands in Queue. 56 | * 57 | * @param[in] command - tuple of 58 | */ 59 | void execute(CommandHandler command); 60 | 61 | private: 62 | /** @brief Check if anything in queue and alert host if so */ 63 | void checkQueueAndAlertHost(); 64 | 65 | /** @brief Call back interface on message timeouts to host. 66 | * 67 | * @detail When this happens, a failure message would be sent 68 | * to all the commands that are in the Queue and queue 69 | * will be purged 70 | */ 71 | void hostTimeout(); 72 | 73 | /** @brief Clears the command queue 74 | * 75 | * @detail Clears the command queue and calls all callbacks 76 | * specifying the command wasn't successful. 77 | */ 78 | void clearQueue(); 79 | 80 | /** @brief Clears the command queue on a power on 81 | * 82 | * @detail The properties changed handler for the 83 | * RequestedHostTransition property. When this property 84 | * changes to 'On', this function will purge the command 85 | * queue. 86 | * 87 | * This is done to avoid having commands that were issued 88 | * before the host powers on from getting sent to the host, 89 | * either due to race conditions around state transitions 90 | * or from a user doing something like requesting an already 91 | * powered off system to power off again and then immediately 92 | * requesting a power on. 93 | * 94 | * @param[in] msg - the sdbusplus message containing the property 95 | */ 96 | void clearQueueOnPowerOn(sdbusplus::message_t& msg); 97 | 98 | /** @brief Reference to the dbus handler */ 99 | sdbusplus::bus_t& bus; 100 | 101 | /** @brief Queue to store the requested commands */ 102 | std::queue workQueue{}; 103 | 104 | /** @brief Timer for commands to host */ 105 | sdbusplus::Timer timer; 106 | 107 | /** @brief Match handler for the requested host state */ 108 | sdbusplus::bus::match_t hostTransitionMatch; 109 | }; 110 | 111 | } // namespace command 112 | } // namespace host 113 | } // namespace phosphor 114 | -------------------------------------------------------------------------------- /host-interface.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #include "host-interface.hpp" 5 | 6 | #include "systemintfcmds.hpp" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace phosphor 19 | { 20 | namespace host 21 | { 22 | namespace command 23 | { 24 | 25 | // When you see Base:: you know we're referencing our base class 26 | namespace Base = sdbusplus::server::xyz::openbmc_project::control; 27 | 28 | // IPMI OEM command. 29 | // https://github.com/openbmc/openbmc/issues/2082 for handling 30 | // Non-OEM commands that need to send SMS_ATN 31 | using OEMCmd = uint8_t; 32 | 33 | // Map of IPMI OEM command to its equivalent interface command. 34 | // This is needed when invoking the callback handler to indicate 35 | // the status of the executed command. 36 | static const std::map intfCommand = { 37 | {CMD_HEARTBEAT, Base::Host::Command::Heartbeat}, 38 | {CMD_POWER, Base::Host::Command::SoftOff}}; 39 | 40 | // Map of Interface command to its corresponding IPMI OEM command. 41 | // This is needed when pushing IPMI commands to command manager's 42 | // queue. The same pair will be returned when IPMI asks us 43 | // why a SMS_ATN was sent 44 | static const std::map ipmiCommand = { 45 | {Base::Host::Command::Heartbeat, std::make_pair(CMD_HEARTBEAT, 0x00)}, 46 | {Base::Host::Command::SoftOff, std::make_pair(CMD_POWER, SOFT_OFF)}}; 47 | 48 | // Called at user request 49 | void Host::execute(Base::Host::Command command) 50 | { 51 | lg2::debug("Pushing cmd on to queue, control host cmd: {CONTROL_HOST_CMD}", 52 | "CONTROL_HOST_CMD", convertForMessage(command)); 53 | 54 | auto cmd = std::make_tuple( 55 | ipmiCommand.at(command), 56 | std::bind(&Host::commandStatusHandler, this, std::placeholders::_1, 57 | std::placeholders::_2)); 58 | 59 | ipmid_send_cmd_to_host(std::move(cmd)); 60 | } 61 | 62 | // Called into by Command Manager 63 | void Host::commandStatusHandler(IpmiCmdData cmd, bool status) 64 | { 65 | // Need to convert to the equivalent one mentioned in spec 66 | auto value = status ? Result::Success : Result::Failure; 67 | 68 | // Fire a signal 69 | this->commandComplete(intfCommand.at(std::get<0>(cmd)), value); 70 | } 71 | 72 | Host::FirmwareCondition Host::currentFirmwareCondition() const 73 | { 74 | // shared object used to wait for host response 75 | auto hostCondition = 76 | std::make_shared>(); 77 | 78 | // callback for command to host 79 | auto hostAckCallback = [hostCondition](IpmiCmdData, bool status) { 80 | auto value = status ? Host::FirmwareCondition::Running 81 | : Host::FirmwareCondition::Off; 82 | 83 | lg2::debug("currentFirmwareCondition:hostAckCallback fired, " 84 | "control host cmd: {CONTROL_HOST_CMD}", 85 | "CONTROL_HOST_CMD", value); 86 | 87 | *(hostCondition.get()) = value; 88 | return; 89 | }; 90 | 91 | auto cmd = phosphor::host::command::CommandHandler( 92 | ipmiCommand.at(Base::Host::Command::Heartbeat), 93 | std::move(hostAckCallback)); 94 | 95 | ipmid_send_cmd_to_host(std::move(cmd)); 96 | 97 | // Timer to ensure this function returns something within a reasonable time 98 | sdbusplus::Timer hostAckTimer([hostCondition]() { 99 | lg2::debug("currentFirmwareCondition: timer expired!"); 100 | *(hostCondition.get()) = Host::FirmwareCondition::Off; 101 | }); 102 | 103 | // Wait 1 second past the ATN_ACK timeout to ensure we wait for as 104 | // long as the timeout 105 | hostAckTimer.start(std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS + 1)); 106 | 107 | auto io = getIoContext(); 108 | 109 | while (!hostCondition.get()->has_value()) 110 | { 111 | lg2::debug("currentFirmwareCondition: waiting for host response"); 112 | io->run_for(std::chrono::milliseconds(100)); 113 | } 114 | hostAckTimer.stop(); 115 | 116 | lg2::debug("currentFirmwareCondition: hostCondition is ready!"); 117 | return hostCondition.get()->value(); 118 | } 119 | 120 | } // namespace command 121 | } // namespace host 122 | } // namespace phosphor 123 | -------------------------------------------------------------------------------- /host-interface.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | namespace phosphor 8 | { 9 | namespace host 10 | { 11 | namespace command 12 | { 13 | 14 | /** @class Host 15 | * @brief OpenBMC control and condition host interface implementation. 16 | * @details A concrete implementation for xyz.openbmc_project.Control.Host 17 | * and xyz.openbmc_project.Condition.HostFirmware DBus API's. 18 | */ 19 | class Host : 20 | public sdbusplus::server::object_t< 21 | sdbusplus::server::xyz::openbmc_project::control::Host, 22 | sdbusplus::server::xyz::openbmc_project::condition::HostFirmware> 23 | { 24 | public: 25 | /** @brief Constructs Host Control and Condition Interfaces 26 | * 27 | * @param[in] bus - The Dbus bus object 28 | * @param[in] objPath - The Dbus object path 29 | */ 30 | Host(sdbusplus::bus_t& bus, const char* objPath) : 31 | sdbusplus::server::object_t< 32 | sdbusplus::server::xyz::openbmc_project::control::Host, 33 | sdbusplus::server::xyz::openbmc_project::condition::HostFirmware>( 34 | bus, objPath) 35 | { 36 | // Nothing to do 37 | } 38 | 39 | /** @brief Send input command to host 40 | * Note that the command will be queued in a FIFO if 41 | * other commands to the host have yet to be run 42 | * 43 | * @param[in] command - Input command to execute 44 | */ 45 | void execute(Command command) override; 46 | 47 | /** @brief Override reads to CurrentFirmwareCondition */ 48 | FirmwareCondition currentFirmwareCondition() const override; 49 | 50 | private: 51 | /** @brief Callback function to be invoked by command manager 52 | * 53 | * @detail Conveys the status of the last Host bound command. 54 | * Depending on the status, a CommandComplete or 55 | * CommandFailure signal would be sent 56 | * 57 | * @param[in] cmd - IPMI command and data sent to Host 58 | * @param[in] status - Success or Failure 59 | */ 60 | void commandStatusHandler(IpmiCmdData cmd, bool status); 61 | }; 62 | 63 | } // namespace command 64 | } // namespace host 65 | } // namespace phosphor 66 | -------------------------------------------------------------------------------- /host-ipmid-whitelist.conf: -------------------------------------------------------------------------------- 1 | #:: 3 | 0x00:0x01 //: 4 | 0x00:0x02 //: 5 | 0x00:0x05 //: 6 | 0x00:0x06 //: 7 | 0x00:0x08 //: 8 | 0x00:0x09 //: 9 | 0x00:0x0F //: 10 | 0x04:0x02 //: 11 | 0x04:0x2D //: 12 | 0x04:0x2F //: 13 | 0x04:0x30 //: 14 | 0x06:0x01 //: 15 | 0x06:0x04 //: 16 | 0x06:0x06 //: 17 | 0x06:0x07 //: 18 | 0x06:0x08 //: 19 | 0x06:0x22 //: 20 | 0x06:0x24 //: 21 | 0x06:0x25 //: 22 | 0x06:0x2E //: 23 | 0x06:0x2F //: 24 | 0x06:0x31 //: 25 | 0x06:0x35 //: 26 | 0x06:0x36 //: 27 | 0x06:0x37 //: 28 | 0x06:0x42 //: 29 | 0x06:0x4D //: 30 | 0x06:0x4E //: 31 | 0x06:0x4F //: 32 | 0x06:0x54 //: 33 | 0x0A:0x10 //: 34 | 0x0A:0x11 //: 35 | 0x0A:0x20 //: 36 | 0x0A:0x22 //: 37 | 0x0A:0x23 //: 38 | 0x0A:0x40 //: 39 | 0x0A:0x42 //: 40 | 0x0A:0x44 //: 41 | 0x0A:0x48 //: 42 | 0x0A:0x49 //: 43 | 0x0A:0x5C //: 44 | 0x0C:0x02 //: 45 | 0x2C:0x00 //: 46 | 0x2C:0x01 //: 47 | 0x2C:0x02 //: 48 | 0x2C:0x03 //: 49 | 0x2C:0x06 //: 50 | 0x2C:0x07 //: 51 | 0x2C:0x10 //: 52 | -------------------------------------------------------------------------------- /include/dbus-sdr/sensorcommands.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | // Copyright (c) 2017 2018 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | #include 19 | 20 | #include 21 | 22 | #pragma pack(push, 1) 23 | 24 | struct SensorThresholdResp 25 | { 26 | uint8_t readable; 27 | uint8_t lowernc; 28 | uint8_t lowercritical; 29 | uint8_t lowernonrecoverable; 30 | uint8_t uppernc; 31 | uint8_t uppercritical; 32 | uint8_t uppernonrecoverable; 33 | }; 34 | 35 | #pragma pack(pop) 36 | 37 | enum class IPMIThresholdRespBits 38 | { 39 | lowerNonCritical, 40 | lowerCritical, 41 | lowerNonRecoverable, 42 | upperNonCritical, 43 | upperCritical, 44 | upperNonRecoverable 45 | }; 46 | 47 | enum class IPMISensorReadingByte2 : uint8_t 48 | { 49 | eventMessagesEnable = (1 << 7), 50 | sensorScanningEnable = (1 << 6), 51 | readingStateUnavailable = (1 << 5), 52 | }; 53 | 54 | enum class IPMISensorReadingByte3 : uint8_t 55 | { 56 | upperNonRecoverable = (1 << 5), 57 | upperCritical = (1 << 4), 58 | upperNonCritical = (1 << 3), 59 | lowerNonRecoverable = (1 << 2), 60 | lowerCritical = (1 << 1), 61 | lowerNonCritical = (1 << 0), 62 | }; 63 | 64 | enum class IPMISensorEventEnableByte2 : uint8_t 65 | { 66 | eventMessagesEnable = (1 << 7), 67 | sensorScanningEnable = (1 << 6), 68 | }; 69 | 70 | enum class IPMISensorEventEnableThresholds : uint8_t 71 | { 72 | nonRecoverableThreshold = (1 << 6), 73 | criticalThreshold = (1 << 5), 74 | nonCriticalThreshold = (1 << 4), 75 | upperNonRecoverableGoingHigh = (1 << 3), 76 | upperNonRecoverableGoingLow = (1 << 2), 77 | upperCriticalGoingHigh = (1 << 1), 78 | upperCriticalGoingLow = (1 << 0), 79 | upperNonCriticalGoingHigh = (1 << 7), 80 | upperNonCriticalGoingLow = (1 << 6), 81 | lowerNonRecoverableGoingHigh = (1 << 5), 82 | lowerNonRecoverableGoingLow = (1 << 4), 83 | lowerCriticalGoingHigh = (1 << 3), 84 | lowerCriticalGoingLow = (1 << 2), 85 | lowerNonCriticalGoingHigh = (1 << 1), 86 | lowerNonCriticalGoingLow = (1 << 0), 87 | }; 88 | 89 | enum class IPMIGetSensorEventEnableThresholds : uint8_t 90 | { 91 | lowerNonCriticalGoingLow = 0, 92 | lowerNonCriticalGoingHigh = 1, 93 | lowerCriticalGoingLow = 2, 94 | lowerCriticalGoingHigh = 3, 95 | lowerNonRecoverableGoingLow = 4, 96 | lowerNonRecoverableGoingHigh = 5, 97 | upperNonCriticalGoingLow = 6, 98 | upperNonCriticalGoingHigh = 7, 99 | upperCriticalGoingLow = 8, 100 | upperCriticalGoingHigh = 9, 101 | upperNonRecoverableGoingLow = 10, 102 | upperNonRecoverableGoingHigh = 11, 103 | }; 104 | 105 | namespace ipmi 106 | { 107 | 108 | uint16_t getNumberOfSensors(); 109 | 110 | SensorSubTree& getSensorTree(); 111 | 112 | ipmi_ret_t getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum, 113 | std::string& connection, std::string& path, 114 | std::vector* interfaces = nullptr); 115 | 116 | struct IPMIThresholds 117 | { 118 | std::optional warningLow; 119 | std::optional warningHigh; 120 | std::optional criticalLow; 121 | std::optional criticalHigh; 122 | }; 123 | 124 | namespace sensor 125 | { 126 | /** 127 | * @brief Retrieve the number of sensors that are not included in the list of 128 | * sensors published via D-Bus 129 | * 130 | * @param[in]: ctx: the pointer to the D-Bus context 131 | * @return: The number of additional sensors separate from those published 132 | * dynamically on D-Bus 133 | */ 134 | size_t getOtherSensorsCount(ipmi::Context::ptr ctx); 135 | 136 | /** 137 | * @brief Retrieve the record data for the sensors not published via D-Bus 138 | * 139 | * @param[in]: ctx: the pointer to the D-Bus context 140 | * @param[in]: recordID: the integer index for the sensor to retrieve 141 | * @param[out]: SDR data for the indexed sensor 142 | * @return: 0: success 143 | * negative number: error condition 144 | */ 145 | int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID, 146 | std::vector& recordData); 147 | } // namespace sensor 148 | 149 | namespace dcmi 150 | { 151 | 152 | struct sensorInfo 153 | { 154 | std::string objectPath; 155 | uint8_t type; 156 | uint16_t recordId; 157 | uint8_t entityId; 158 | uint8_t entityInstance; 159 | }; 160 | 161 | } // namespace dcmi 162 | 163 | } // namespace ipmi 164 | -------------------------------------------------------------------------------- /include/dbus-sdr/sensorutils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | // Copyright (c) 2017 2018 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace ipmi 24 | { 25 | static constexpr int16_t maxInt10 = 0x1FF; 26 | static constexpr int16_t minInt10 = -0x200; 27 | static constexpr int8_t maxInt4 = 7; 28 | static constexpr int8_t minInt4 = -8; 29 | 30 | bool getSensorAttributes(const double max, const double min, int16_t& mValue, 31 | int8_t& rExp, int16_t& bValue, int8_t& bExp, 32 | bool& bSigned); 33 | 34 | uint8_t scaleIPMIValueFromDouble(const double value, const int16_t mValue, 35 | const int8_t rExp, const int16_t bValue, 36 | const int8_t bExp, const bool bSigned); 37 | 38 | uint8_t getScaledIPMIValue(const double value, const double max, 39 | const double min); 40 | } // namespace ipmi 41 | -------------------------------------------------------------------------------- /include/dbus-sdr/storagecommands.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | // Copyright (c) 2017 2018 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | #include "sensorhandler.hpp" 19 | 20 | #include 21 | 22 | static constexpr uint8_t ipmiSdrVersion = 0x51; 23 | 24 | namespace dynamic_sensors::ipmi::sel 25 | { 26 | static constexpr uint8_t selOperationSupport = 0x02; 27 | static constexpr uint8_t systemEvent = 0x02; 28 | static constexpr size_t systemEventSize = 3; 29 | static constexpr uint8_t oemTsEventFirst = 0xC0; 30 | static constexpr uint8_t oemTsEventLast = 0xDF; 31 | static constexpr size_t oemTsEventSize = 9; 32 | static constexpr uint8_t oemEventFirst = 0xE0; 33 | static constexpr uint8_t oemEventLast = 0xFF; 34 | static constexpr size_t oemEventSize = 13; 35 | static constexpr uint8_t eventMsgRev = 0x04; 36 | } // namespace dynamic_sensors::ipmi::sel 37 | 38 | enum class SdrRepositoryInfoOps : uint8_t 39 | { 40 | allocCommandSupported = 0x1, 41 | reserveSDRRepositoryCommandSupported = 0x2, 42 | partialAddSDRSupported = 0x4, 43 | deleteSDRSupported = 0x8, 44 | reserved = 0x10, 45 | modalLSB = 0x20, 46 | modalMSB = 0x40, 47 | overflow = 0x80 48 | }; 49 | 50 | enum class GetFRUAreaAccessType : uint8_t 51 | { 52 | byte = 0x0, 53 | words = 0x1 54 | }; 55 | 56 | enum class SensorUnits : uint8_t 57 | { 58 | unspecified = 0x0, 59 | degreesC = 0x1, 60 | volts = 0x4, 61 | amps = 0x5, 62 | watts = 0x6, 63 | joules = 0x7, 64 | rpm = 0x12, 65 | }; 66 | 67 | #pragma pack(push, 1) 68 | struct FRUHeader 69 | { 70 | uint8_t commonHeaderFormat; 71 | uint8_t internalOffset; 72 | uint8_t chassisOffset; 73 | uint8_t boardOffset; 74 | uint8_t productOffset; 75 | uint8_t multiRecordOffset; 76 | uint8_t pad; 77 | uint8_t checksum; 78 | }; 79 | #pragma pack(pop) 80 | 81 | #pragma pack(push, 1) 82 | struct Type12Record 83 | { 84 | get_sdr::SensorDataRecordHeader header; 85 | uint8_t targetAddress; 86 | uint8_t channelNumber; 87 | uint8_t powerStateNotification; 88 | uint8_t deviceCapabilities; 89 | // define reserved bytes explicitly. The uint24_t is silently expanded to 90 | // uint32_t, which ruins the byte alignment required by this structure. 91 | uint8_t reserved[3]; 92 | uint8_t entityID; 93 | uint8_t entityInstance; 94 | uint8_t oem; 95 | uint8_t typeLengthCode; 96 | char name[16]; 97 | 98 | Type12Record(uint16_t recordID, uint8_t address, uint8_t chNumber, 99 | uint8_t pwrStateNotification, uint8_t capabilities, 100 | uint8_t eid, uint8_t entityInst, uint8_t mfrDefined, 101 | const std::string& sensorname) : 102 | targetAddress(address), channelNumber(chNumber), 103 | powerStateNotification(pwrStateNotification), 104 | deviceCapabilities(capabilities), reserved{}, entityID(eid), 105 | entityInstance(entityInst), oem(mfrDefined) 106 | { 107 | get_sdr::header::set_record_id(recordID, &header); 108 | header.sdr_version = ipmiSdrVersion; 109 | header.record_type = 0x12; 110 | size_t nameLen = std::min(sensorname.size(), sizeof(name)); 111 | header.record_length = 112 | sizeof(Type12Record) - sizeof(get_sdr::SensorDataRecordHeader) - 113 | sizeof(name) + nameLen; 114 | typeLengthCode = 0xc0 | nameLen; 115 | std::copy(sensorname.begin(), sensorname.begin() + nameLen, name); 116 | } 117 | }; 118 | #pragma pack(pop) 119 | 120 | namespace ipmi 121 | { 122 | namespace storage 123 | { 124 | 125 | constexpr const size_t type12Count = 2; 126 | ipmi_ret_t getFruSdrs(ipmi::Context::ptr ctx, size_t index, 127 | get_sdr::SensorDataFruRecord& resp); 128 | 129 | ipmi_ret_t getFruSdrCount(ipmi::Context::ptr ctx, size_t& count); 130 | 131 | std::vector getType8SDRs( 132 | ipmi::sensor::EntityInfoMap::const_iterator& entity, uint16_t recordId); 133 | std::vector getType12SDRs(uint16_t index, uint16_t recordId); 134 | std::vector getNMDiscoverySDR(uint16_t index, uint16_t recordId); 135 | } // namespace storage 136 | } // namespace ipmi 137 | -------------------------------------------------------------------------------- /include/ipmid-host/cmd-utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace phosphor 10 | { 11 | namespace host 12 | { 13 | namespace command 14 | { 15 | /** @detail After sending SMS_ATN to the Host, Host comes down and 16 | * asks why an 'SMS_ATN` was sent. 17 | * BMC then sends 'There is a Message to be Read` as response. 18 | * Host then comes down asks for Message and the specified 19 | * commands and data would go as data conforming to IPMI spec. 20 | * 21 | * Refer: 6.13.2 Send Message Command From System Interface 22 | * in IPMI V2.0 spec. 23 | */ 24 | 25 | /** @brief IPMI command */ 26 | using IPMIcmd = uint8_t; 27 | 28 | /** @brief Data associated with command */ 29 | using Data = uint8_t; 30 | 31 | /** @brief to be sent as payload when Host asks for 32 | * the message that can be associated with the previous SMS_ATN 33 | */ 34 | using IpmiCmdData = std::pair; 35 | 36 | /** @detail Implementation specific callback function to be invoked 37 | * conveying the status of the executed command. Specific 38 | * implementations may then broadcast an agreed signal 39 | */ 40 | using CallBack = std::function; 41 | 42 | /** @detail Tuple encapsulating above 2 to enable using Manager by 43 | * different implementations. Users of Manager will supply 44 | * along with the callback handler. 45 | * Manager will invoke the handler onveying the status of 46 | * the command. 47 | */ 48 | using CommandHandler = std::tuple; 49 | 50 | } // namespace command 51 | } // namespace host 52 | } // namespace phosphor 53 | -------------------------------------------------------------------------------- /include/ipmid-host/cmd.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // Global Host Bound Command manager 7 | extern void ipmid_send_cmd_to_host(phosphor::host::command::CommandHandler&&); 8 | extern std::unique_ptr& 9 | ipmid_get_sdbus_plus_handler(); 10 | -------------------------------------------------------------------------------- /include/ipmid/api.h: -------------------------------------------------------------------------------- 1 | #ifndef __HOST_IPMID_IPMI_COMMON_H__ 2 | #define __HOST_IPMID_IPMI_COMMON_H__ 3 | 4 | #include 5 | 6 | #include 7 | 8 | /* 9 | * Specifies the minimum privilege level required to execute the command 10 | * This means the command can be executed at a given privilege level or higher 11 | * privilege level. Those commands which can be executed via system interface 12 | * only should use SYSTEM_INTERFACE 13 | */ 14 | enum CommandPrivilege 15 | { 16 | PRIVILEGE_CALLBACK = 0x01, 17 | PRIVILEGE_USER, 18 | PRIVILEGE_OPERATOR, 19 | PRIVILEGE_ADMIN, 20 | PRIVILEGE_OEM, 21 | SYSTEM_INTERFACE = 0xFF, 22 | }; 23 | 24 | // IPMI Net Function number as specified by IPMI V2.0 spec. 25 | // Example : 26 | // NETFUN_APP = (0x06 << 2), 27 | typedef unsigned char ipmi_netfn_t; 28 | 29 | // IPMI Command for a Net Function number as specified by IPMI V2.0 spec. 30 | typedef unsigned char ipmi_cmd_t; 31 | 32 | // Buffer containing data from sender of netfn and command as part of request 33 | typedef void* ipmi_request_t; 34 | 35 | // This is the response buffer that the provider of [netfn,cmd] will send back 36 | // to the caller. Provider will allocate the memory inside the handler and then 37 | // will do a memcpy to this response buffer and also will set the data size 38 | // parameter to the size of the buffer. 39 | // EXAMPLE : 40 | // unsigned char str[] = {0x00, 0x01, 0xFE, 0xFF, 0x0A, 0x01}; 41 | // *data_len = 6; 42 | // memcpy(response, &str, *data_len); 43 | typedef void* ipmi_response_t; 44 | 45 | // This buffer contains any *user specific* data that is of interest only to the 46 | // plugin. For a ipmi function router, this data is opaque. At the time of 47 | // registering the plugin handlers, plugin may optionally allocate a memory and 48 | // fill in whatever needed that will be of help during the actual handling of 49 | // command. IPMID will just pass the netfn, cmd and also this data to plugins 50 | // during the command handler invocation. 51 | typedef void* ipmi_context_t; 52 | 53 | // Length of request / response buffer depending on whether the data is a 54 | // request or a response from a plugin handler. 55 | typedef std::size_t* ipmi_data_len_t; 56 | 57 | // Plugin function return the status code 58 | typedef unsigned char ipmi_ret_t; 59 | 60 | typedef enum CommandPrivilege ipmi_cmd_privilege_t; 61 | 62 | // This is the callback handler that the plugin registers with IPMID. IPMI 63 | // function router will then make a call to this callback handler with the 64 | // necessary arguments of netfn, cmd, request, response, size and context. 65 | typedef ipmi_ret_t (*ipmid_callback_t)(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, 66 | ipmi_response_t, ipmi_data_len_t, 67 | ipmi_context_t); 68 | 69 | // This is the constructor function that is called into by each plugin handlers. 70 | // When ipmi sets up the callback handlers, a call is made to this with 71 | // information of netfn, cmd, callback handler pointer and context data. 72 | void ipmi_register_callback(ipmi_netfn_t, ipmi_cmd_t, ipmi_context_t, 73 | ipmid_callback_t, ipmi_cmd_privilege_t); 74 | 75 | unsigned short reserveSel(void); 76 | bool checkSELReservation(unsigned short id); 77 | void cancelSELReservation(void); 78 | 79 | // These are the command network functions, the response 80 | // network functions are the function + 1. So to determine 81 | // the proper network function which issued the command 82 | // associated with a response, subtract 1. 83 | // Note: these are also shifted left to make room for the LUN. 84 | enum ipmi_net_fns 85 | { 86 | NETFUN_CHASSIS = 0x00, 87 | NETFUN_BRIDGE = 0x02, 88 | NETFUN_SENSOR = 0x04, 89 | NETFUN_APP = 0x06, 90 | NETFUN_FIRMWARE = 0x08, 91 | NETFUN_STORAGE = 0x0a, 92 | NETFUN_TRANSPORT = 0x0c, 93 | NETFUN_GRPEXT = 0x2c, 94 | NETFUN_OEM_GROUP = 0x2e, 95 | NETFUN_NONE = 0x30, 96 | NETFUN_OEM = 0x32, 97 | NETFUN_IBM_OEM = 0x3A 98 | }; 99 | 100 | // Return (completion) codes from a IPMI operation as needed by IPMI V2.0 spec. 101 | enum ipmi_return_codes 102 | { 103 | IPMI_CC_OK = 0x00, 104 | IPMI_DCMI_CC_NO_ACTIVE_POWER_LIMIT = 0x80, 105 | IPMI_WDOG_CC_NOT_INIT = 0x80, 106 | IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED = 0x80, 107 | IPMI_CC_SYSTEM_INFO_PARAMETER_SET_READ_ONLY = 0x82, 108 | IPMI_CC_BUSY = 0xC0, 109 | IPMI_CC_INVALID = 0xC1, 110 | IPMI_CC_TIMEOUT = 0xC3, 111 | IPMI_CC_OUT_OF_SPACE = 0xC4, 112 | IPMI_CC_INVALID_RESERVATION_ID = 0xC5, 113 | IPMI_CC_REQ_DATA_TRUNCATED = 0xC6, 114 | IPMI_CC_REQ_DATA_LEN_INVALID = 0xC7, 115 | IPMI_CC_PARM_OUT_OF_RANGE = 0xC9, 116 | IPMI_CC_REQUESTED_TOO_MANY_BYTES = 0xCA, 117 | IPMI_CC_SENSOR_INVALID = 0xCB, 118 | IPMI_CC_INVALID_FIELD_REQUEST = 0xCC, 119 | IPMI_CC_ILLEGAL_COMMAND = 0xCD, 120 | IPMI_CC_RESPONSE_ERROR = 0xCE, 121 | IPMI_CC_INSUFFICIENT_PRIVILEGE = 0xD4, 122 | IPMI_CC_UNSPECIFIED_ERROR = 0xFF, 123 | }; 124 | 125 | // Temp solution: To detect the request source channel. 126 | // There is no stright forward way to get the exact 127 | // channel so we are hardcoding it to KCS in case host-ipmid 128 | // and LAN1 for netipmid. 129 | // we can't differentiate between LAN1 & LAN2 for netipmid in this logic. 130 | // As our current design will not be able to support the same. This is done 131 | // so that in all the places where ever we need to use the self channel can be 132 | // be implemented properly and based on new architecture.this can be updated. 133 | typedef enum 134 | { 135 | interfaceKCS = 0, 136 | interfaceLAN1 = 1, 137 | interfaceUnknown = 0xFF 138 | } EInterfaceIndex; 139 | 140 | EInterfaceIndex getInterfaceIndex(void); 141 | 142 | sd_bus* ipmid_get_sd_bus_connection(void); 143 | sd_event* ipmid_get_sd_event_connection(void); 144 | 145 | // move this from ipmid.hpp, which is now gone 146 | // this should not be used. Use the channel API to get the channel size 147 | #define MAX_IPMI_BUFFER 64 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /include/ipmid/api.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2018 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #pragma once 18 | 19 | #define ALLOW_DEPRECATED_API 1 20 | // make it possible to ONLY include api.hpp during the transition 21 | #ifdef ALLOW_DEPRECATED_API 22 | #include 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | // any client can interact with the main asio context 34 | std::shared_ptr getIoContext(); 35 | 36 | // any client can interact with the main sdbus 37 | std::shared_ptr getSdBus(); 38 | 39 | /** 40 | * @brief post some work to the async exection queue 41 | * 42 | * The IPMI daemon runs an async exection queue; this allows any function to 43 | * pass in work to be executed in that context 44 | * 45 | * @tparam WorkFn - a function of type void(void) 46 | * @param work - the callback function to be executed 47 | */ 48 | template 49 | static inline void post_work(WorkFn work) 50 | { 51 | boost::asio::post(*getIoContext(), std::forward(work)); 52 | } 53 | 54 | enum class SignalResponse : int 55 | { 56 | breakExecution, 57 | continueExecution, 58 | }; 59 | 60 | /** 61 | * @brief add a signal handler 62 | * 63 | * This registers a handler to be called asynchronously via the execution 64 | * queue when the specified signal is received. 65 | * 66 | * Priority allows a signal handler to specify what order in the handler 67 | * chain it gets called. Lower priority numbers will cause the handler to 68 | * be executed later in the chain, while the highest priority numbers will cause 69 | * the handler to be executed first. 70 | * 71 | * In order to facilitate a chain of handlers, each handler in the chain will be 72 | * able to return breakExecution or continueExecution. Returning breakExecution 73 | * will break the chain and no further handlers will execute for that signal. 74 | * Returning continueExecution will allow lower-priority handlers to execute. 75 | * 76 | * By default, the main asio execution loop will register a low priority 77 | * (prioOpenBmcBase) handler for SIGINT and SIGTERM to cause the process to stop 78 | * on either of those signals. To prevent one of those signals from causing the 79 | * process to stop, simply register a higher priority handler that returns 80 | * breakExecution. 81 | * 82 | * @param int - priority of handler 83 | * @param int - signal number to wait for 84 | * @param handler - the callback function to be executed 85 | */ 86 | void registerSignalHandler(int priority, int signalNumber, 87 | const std::function& handler); 88 | -------------------------------------------------------------------------------- /include/ipmid/entity_map_json.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace ipmi 9 | { 10 | namespace sensor 11 | { 12 | 13 | /** 14 | * @brief Grab a handle to the entity map. 15 | */ 16 | const EntityInfoMap& getIpmiEntityRecords(); 17 | 18 | /** 19 | * @brief Open the default entity map json file, and if present and valid json, 20 | * return a built entity map. 21 | * 22 | * @return the map 23 | */ 24 | EntityInfoMap buildEntityMapFromFile(); 25 | 26 | /** 27 | * @brief Given json data validate the data matches the expected format for the 28 | * entity map configuration and parse the data into a map of the entities. 29 | * 30 | * If any entry is invalid, the entire contents passed in is disregarded as 31 | * possibly corrupt. 32 | * 33 | * @param[in] data - the json data 34 | * @return the map 35 | */ 36 | EntityInfoMap buildJsonEntityMap(const nlohmann::json& data); 37 | 38 | /** 39 | * @brief Owner of the EntityInfoMap. 40 | */ 41 | class EntityInfoMapContainer 42 | { 43 | public: 44 | /** Get ahold of the owner. */ 45 | static EntityInfoMapContainer* getContainer(); 46 | /** Get ahold of the records. */ 47 | const EntityInfoMap& getIpmiEntityRecords(); 48 | 49 | private: 50 | EntityInfoMapContainer(const EntityInfoMap& entityRecords) : 51 | entityRecords(entityRecords) 52 | {} 53 | EntityInfoMap entityRecords; 54 | }; 55 | 56 | } // namespace sensor 57 | } // namespace ipmi 58 | -------------------------------------------------------------------------------- /include/ipmid/filter.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © 2018 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace ipmi 28 | { 29 | 30 | using FilterFunction = ipmi::Cc(ipmi::message::Request::ptr); 31 | 32 | /** 33 | * @brief Filter base class for dealing with IPMI request/response 34 | * 35 | * The subclasses are all templated so they can provide access to any type of 36 | * command callback functions. 37 | */ 38 | class FilterBase 39 | { 40 | public: 41 | using ptr = std::shared_ptr; 42 | 43 | virtual ~FilterBase() = default; 44 | 45 | virtual ipmi::Cc call(message::Request::ptr request) = 0; 46 | }; 47 | 48 | /** 49 | * @brief filter concrete class 50 | * 51 | * This is the base template that ipmi filters will resolve into. This is 52 | * essentially just a wrapper to hold the filter callback so it can be stored in 53 | * the filter list. 54 | * 55 | * Filters are called with a ipmi::message::Request shared_ptr on all IPMI 56 | * commands in priority order and each filter has the opportunity to reject the 57 | * command (by returning an IPMI error competion code.) If all the filters 58 | * return success, the actual IPMI command will be executed. Filters can reject 59 | * the command for any reason, based on system state, the context, the command 60 | * payload, etc. 61 | */ 62 | template 63 | class IpmiFilter : public FilterBase 64 | { 65 | public: 66 | IpmiFilter(Filter&& filter) : filter_(std::move(filter)) {} 67 | 68 | ipmi::Cc call(message::Request::ptr request) override 69 | { 70 | return filter_(request); 71 | } 72 | 73 | private: 74 | Filter filter_; 75 | }; 76 | 77 | /** 78 | * @brief helper function to construct a filter object 79 | * 80 | * This is called internally by the ipmi::registerFilter function. 81 | */ 82 | template 83 | static inline auto makeFilter(Filter&& filter) 84 | { 85 | FilterBase::ptr ptr(new IpmiFilter(std::forward(filter))); 86 | return ptr; 87 | } 88 | template 89 | static inline auto makeFilter(const Filter& filter) 90 | { 91 | Filter lFilter = filter; 92 | return makeFilter(std::forward(lFilter)); 93 | } 94 | 95 | namespace impl 96 | { 97 | 98 | // IPMI command filter registration implementation 99 | void registerFilter(int prio, ::ipmi::FilterBase::ptr filter); 100 | 101 | } // namespace impl 102 | 103 | /** 104 | * @brief IPMI command filter registration function 105 | * 106 | * This function should be used to register IPMI command filter functions. 107 | * This function just passes the callback to makeFilter, which creates a 108 | * wrapper functor object that ultimately calls the callback. 109 | * 110 | * Filters are called with a ipmi::message::Request shared_ptr on all IPMI 111 | * commands in priority order and each filter has the opportunity to reject the 112 | * command (by returning an IPMI error competion code.) If all the filters 113 | * return success, the actual IPMI command will be executed. Filters can reject 114 | * the command for any reason, based on system state, the context, the command 115 | * payload, etc. 116 | * 117 | * @param prio - priority at which to register; see api.hpp 118 | * @param filter - the callback function that will handle this request 119 | * 120 | * @return bool - success of registering the handler 121 | */ 122 | template 123 | void registerFilter(int prio, Filter&& filter) 124 | { 125 | auto f = ipmi::makeFilter(std::forward(filter)); 126 | impl::registerFilter(prio, f); 127 | } 128 | 129 | template 130 | void registerFilter(int prio, const Filter& filter) 131 | { 132 | auto f = ipmi::makeFilter(filter); 133 | impl::registerFilter(prio, f); 134 | } 135 | 136 | } // namespace ipmi 137 | -------------------------------------------------------------------------------- /include/ipmid/iana.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace oem 6 | { 7 | using Number = std::uint32_t; // smallest standard size >= 24. 8 | 9 | /* 10 | * This is the OpenBMC IANA OEM Number 11 | */ 12 | constexpr Number obmcOemNumber = 49871; 13 | 14 | /* 15 | * This is the Google IANA OEM Number 16 | */ 17 | constexpr Number googOemNumber = 11129; 18 | 19 | } // namespace oem 20 | -------------------------------------------------------------------------------- /include/ipmid/message/types.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © 2018 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #if BOOST_VERSION < 107900 26 | using bitcount_t = unsigned; 27 | #else 28 | using bitcount_t = std::size_t; 29 | #endif 30 | 31 | // unsigned fixed-bit sizes 32 | template 33 | using fixed_uint_t = 34 | boost::multiprecision::number>; 37 | // signed fixed-bit sizes 38 | template 39 | using fixed_int_t = 40 | boost::multiprecision::number>; 43 | 44 | using uint1_t = fixed_uint_t<1>; 45 | using uint2_t = fixed_uint_t<2>; 46 | using uint3_t = fixed_uint_t<3>; 47 | using uint4_t = fixed_uint_t<4>; 48 | using uint5_t = fixed_uint_t<5>; 49 | using uint6_t = fixed_uint_t<6>; 50 | using uint7_t = fixed_uint_t<7>; 51 | // native uint8_t 52 | using uint9_t = fixed_uint_t<9>; 53 | using uint10_t = fixed_uint_t<10>; 54 | using uint11_t = fixed_uint_t<11>; 55 | using uint12_t = fixed_uint_t<12>; 56 | using uint13_t = fixed_uint_t<13>; 57 | using uint14_t = fixed_uint_t<14>; 58 | using uint15_t = fixed_uint_t<15>; 59 | // native uint16_t 60 | using uint24_t = fixed_uint_t<24>; 61 | 62 | // signed fixed-bit sizes 63 | using int2_t = fixed_int_t<2>; 64 | using int3_t = fixed_int_t<3>; 65 | using int4_t = fixed_int_t<4>; 66 | using int5_t = fixed_int_t<5>; 67 | using int6_t = fixed_int_t<6>; 68 | using int7_t = fixed_int_t<7>; 69 | // native int8_t 70 | using int9_t = fixed_int_t<9>; 71 | using int10_t = fixed_int_t<10>; 72 | using int11_t = fixed_int_t<11>; 73 | using int12_t = fixed_int_t<12>; 74 | using int13_t = fixed_int_t<13>; 75 | using int14_t = fixed_int_t<14>; 76 | using int15_t = fixed_int_t<15>; 77 | // native int16_t 78 | using int24_t = fixed_int_t<24>; 79 | 80 | // bool is more efficient than a uint1_t 81 | using bit = bool; 82 | 83 | // Mechanism for going from uint7_t, int7_t, or std::bitset<7> to 7 bits 84 | // use nrFixedBits or nrFixedBits 85 | namespace types 86 | { 87 | namespace details 88 | { 89 | 90 | template 91 | struct Size 92 | { 93 | static constexpr bitcount_t value = N; 94 | }; 95 | 96 | template 97 | constexpr auto getNrBits(const fixed_int_t&) -> Size; 98 | template 99 | constexpr auto getNrBits(const fixed_uint_t&) -> Size; 100 | template 101 | constexpr auto getNrBits(const std::bitset&) -> Size; 102 | 103 | template 104 | using underlying_t = 105 | typename std::conditional_t, std::underlying_type, 106 | std::enable_if>::type; 107 | 108 | } // namespace details 109 | 110 | /** 111 | * @brief mechanism to get N from a type like fixed_int_t 112 | * 113 | * helper template to extract N from a fixed_(u)int_t variable 114 | * 115 | * @tparam T - a type of fixed_int_t or fixed_unint_t 116 | * 117 | * @return size_t - evaluates to a constexpr size_t of N 118 | */ 119 | template 120 | constexpr auto nrFixedBits = 121 | decltype(details::getNrBits(std::declval()))::value; 122 | 123 | /** 124 | * @brief Converts a number or enum class to another 125 | * @tparam R - The output type 126 | * @tparam T - The input type 127 | * @param t - An enum or integer value to cast 128 | * @return The value in R form 129 | */ 130 | template 131 | inline R enum_cast(T t) 132 | { 133 | auto tu = static_cast>(t); 134 | auto ru = static_cast>(tu); 135 | return static_cast(ru); 136 | } 137 | 138 | } // namespace types 139 | -------------------------------------------------------------------------------- /include/ipmid/oemopenbmc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace oem 8 | { 9 | /* 10 | * OpenBMC OEM Extension IPMI Command codes. 11 | */ 12 | enum Cmd 13 | { 14 | gpioCmd = 1, 15 | i2cCmd = 2, 16 | flashCmd = 3, 17 | fanManualCmd = 4, 18 | ethStatsCmd = 48, 19 | blobTransferCmd = 128, 20 | }; 21 | 22 | } // namespace oem 23 | -------------------------------------------------------------------------------- /include/ipmid/oemrouter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace oem 14 | { 15 | constexpr std::size_t groupMagicSize = 3; 16 | 17 | using Group = std::array; 18 | 19 | // Handler signature includes ipmi cmd to support wildcard cmd match. 20 | // Buffers and lengths exclude the OemGroup bytes in the IPMI message. 21 | // dataLen supplies length of reqBuf upon call, and should be set to the 22 | // length of replyBuf upon return - conventional in this code base. 23 | using Handler = std::function; // dataLen 27 | 28 | /// Router Interface class. 29 | /// @brief Abstract Router Interface 30 | class Router 31 | { 32 | public: 33 | virtual ~Router() {} 34 | 35 | /// Enable message routing to begin. 36 | virtual void activate() = 0; 37 | 38 | /// Register a handler for given OEMNumber & cmd. 39 | /// Use cmdWildcard to catch any unregistered cmd 40 | /// for the given OEMNumber. 41 | /// 42 | /// @param[in] oen - the OEM Number. 43 | /// @param[in] cmd - the Command. 44 | /// @param[in] handler - the handler to call given that OEN and 45 | /// command. 46 | virtual void registerHandler(Number oen, ipmi_cmd_t cmd, 47 | Handler handler) = 0; 48 | }; 49 | 50 | /// Expose mutable Router for configuration & activation. 51 | /// 52 | /// @returns pointer to OEM Router to use. 53 | Router* mutableRouter(); 54 | 55 | /// Convert a group to an OEN. 56 | /// 57 | /// @param[in] oeg - request buffer for IPMI command. 58 | /// @return the OEN. 59 | constexpr Number toOemNumber(const std::uint8_t oeg[groupMagicSize]) 60 | { 61 | return (oeg[2] << 16) | (oeg[1] << 8) | oeg[0]; 62 | } 63 | 64 | /// Given a Group convert to an OEN. 65 | /// 66 | /// @param[in] oeg - OEM Group reference. 67 | /// @return the OEN. 68 | constexpr Number toOemNumber(const Group& oeg) 69 | { 70 | return (oeg[2] << 16) | (oeg[1] << 8) | oeg[0]; 71 | } 72 | 73 | /// Given an OEN, conver to the OEM Group. 74 | /// 75 | /// @param[in] oen - the OEM Number. 76 | /// @return the OEM Group. 77 | constexpr Group toOemGroup(Number oen) 78 | { 79 | return Group{static_cast(oen), 80 | static_cast(oen >> 8), 81 | static_cast(oen >> 16)}; 82 | } 83 | 84 | } // namespace oem 85 | -------------------------------------------------------------------------------- /include/ipmid/sessiondef.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | namespace session 23 | { 24 | 25 | static constexpr auto sessionManagerRootPath = 26 | "/xyz/openbmc_project/ipmi/session"; 27 | static constexpr auto sessionIntf = "xyz.openbmc_project.Ipmi.SessionInfo"; 28 | static constexpr uint8_t ipmi20VerSession = 0x01; 29 | static constexpr size_t maxSessionCountPerChannel = 15; 30 | static constexpr size_t sessionZero = 0; 31 | static constexpr size_t maxSessionlessCount = 1; 32 | static constexpr uint8_t invalidSessionID = 0; 33 | static constexpr uint8_t invalidSessionHandle = 0; 34 | static constexpr uint8_t defaultSessionHandle = 0xFF; 35 | static constexpr uint8_t maxNetworkInstanceSupported = 4; 36 | static constexpr uint8_t ccInvalidSessionId = 0x87; 37 | static constexpr uint8_t ccInvalidSessionHandle = 0x88; 38 | static constexpr uint8_t searchCurrentSession = 0; 39 | static constexpr uint8_t searchSessionByHandle = 0xFE; 40 | static constexpr uint8_t searchSessionById = 0xFF; 41 | // MSB BIT 7 BIT 6 assigned for netipmid instance in session handle. 42 | static constexpr uint8_t multiIntfaceSessionHandleMask = 0x3F; 43 | 44 | // MSB BIT 31-BIT30 assigned for netipmid instance in session ID 45 | static constexpr uint32_t multiIntfaceSessionIDMask = 0x3FFFFFFF; 46 | 47 | enum class State : uint8_t 48 | { 49 | inactive, // Session is not in use 50 | setupInProgress, // Session Setup Sequence is progressing 51 | active, // Session is active 52 | tearDownInProgress, // When Closing Session 53 | }; 54 | 55 | } // namespace session 56 | -------------------------------------------------------------------------------- /include/ipmid/sessionhelper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief parse session input payload. 9 | * 10 | * This function retrives the session id and session handle from the session 11 | * object path. 12 | * A valid object path will be in the form 13 | * "/xyz/openbmc_project/ipmi/session/channel/sessionId_sessionHandle" 14 | * 15 | * Ex: "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a" 16 | * SessionId : 0X12a4567d 17 | * SessionHandle: 0X8a 18 | 19 | * @param[in] objectPath - session object path 20 | * @param[in] sessionId - retrived session id will be asigned. 21 | * @param[in] sessionHandle - retrived session handle will be asigned. 22 | * 23 | * @return true if session id and session handle are retrived else returns 24 | * false. 25 | */ 26 | bool parseCloseSessionInputPayload(const std::string& objectPath, 27 | uint32_t& sessionId, uint8_t& sessionHandle) 28 | { 29 | if (objectPath.empty()) 30 | { 31 | return false; 32 | } 33 | // getting the position of session id and session handle string from 34 | // object path. 35 | std::size_t ptrPosition = objectPath.rfind("/"); 36 | uint16_t tempSessionHandle = 0; 37 | 38 | if (ptrPosition != std::string::npos) 39 | { 40 | // get the sessionid & session handle string from the session object 41 | // path Ex: sessionIdString: "12a4567d_8a" 42 | std::string sessionIdString = objectPath.substr(ptrPosition + 1); 43 | std::size_t pos = sessionIdString.rfind("_"); 44 | 45 | if (pos != std::string::npos) 46 | { 47 | // extracting the session handle 48 | std::string sessionHandleString = sessionIdString.substr(pos + 1); 49 | // extracting the session id 50 | sessionIdString = sessionIdString.substr(0, pos); 51 | // converting session id string and session handle string to 52 | // hexadecimal. 53 | std::stringstream handle(sessionHandleString); 54 | handle >> std::hex >> tempSessionHandle; 55 | sessionHandle = tempSessionHandle & 0xFF; 56 | std::stringstream idString(sessionIdString); 57 | idString >> std::hex >> sessionId; 58 | return true; 59 | } 60 | } 61 | return false; 62 | } 63 | 64 | /** 65 | * @brief is session object matched. 66 | * 67 | * This function checks whether the objectPath contains reqSessionId and 68 | * reqSessionHandle, e.g., "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a" 69 | * matches sessionId 0x12a4567d and sessionHandle 0x8a. 70 | * 71 | * @param[in] objectPath - session object path 72 | * @param[in] reqSessionId - request session id 73 | * @param[in] reqSessionHandle - request session handle 74 | * 75 | * @return true if the object is matched else return false 76 | **/ 77 | bool isSessionObjectMatched(const std::string objectPath, 78 | const uint32_t reqSessionId, 79 | const uint8_t reqSessionHandle) 80 | { 81 | uint32_t sessionId = 0; 82 | uint8_t sessionHandle = 0; 83 | 84 | if (parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle)) 85 | { 86 | return (reqSessionId == sessionId) || 87 | (reqSessionHandle == sessionHandle); 88 | } 89 | 90 | return false; 91 | } 92 | -------------------------------------------------------------------------------- /include/meson.build: -------------------------------------------------------------------------------- 1 | install_subdir( 2 | 'dbus-sdr', 3 | install_dir: get_option('includedir'), 4 | strip_directory: false, 5 | exclude_files: '*.cpp', 6 | ) 7 | 8 | install_subdir( 9 | 'ipmid', 10 | install_dir: get_option('includedir'), 11 | strip_directory: false, 12 | exclude_files: '*.cpp', 13 | ) 14 | 15 | install_subdir( 16 | 'ipmid-host', 17 | install_dir: get_option('includedir'), 18 | strip_directory: false, 19 | exclude_files: '*.cpp', 20 | ) 21 | 22 | # install the ipmid-host and ipmid includes 23 | install_subdir('ipmid-host', install_dir: get_option('includedir')) 24 | install_subdir('ipmid', install_dir: get_option('includedir')) 25 | -------------------------------------------------------------------------------- /ipmi_fru_info_area.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace ipmi 9 | { 10 | namespace fru 11 | { 12 | using FruAreaData = std::vector; 13 | using Section = std::string; 14 | using Value = std::string; 15 | using Property = std::string; 16 | using PropertyMap = std::map; 17 | using FruInventoryData = std::map; 18 | 19 | /** 20 | * @brief Builds Fru area data from inventory data 21 | * 22 | * @param[in] invData FRU properties values read from inventory 23 | * 24 | * @return FruAreaData FRU area data as per IPMI specification 25 | */ 26 | FruAreaData buildFruAreaData(const FruInventoryData& inventory); 27 | 28 | } // namespace fru 29 | } // namespace ipmi 30 | -------------------------------------------------------------------------------- /ipmiallowlist.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using netfncmd_pair = std::pair; 7 | 8 | extern const std::vector allowlist; 9 | -------------------------------------------------------------------------------- /libipmid/entity_map_json.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace ipmi 12 | { 13 | namespace sensor 14 | { 15 | 16 | EntityInfoMapContainer* EntityInfoMapContainer::getContainer() 17 | { 18 | static std::unique_ptr instance; 19 | 20 | if (!instance) 21 | { 22 | /* TODO: With multi-threading this would all need to be locked so 23 | * the first thread to hit it would set it up. 24 | */ 25 | EntityInfoMap builtEntityMap = buildEntityMapFromFile(); 26 | instance = std::unique_ptr( 27 | new EntityInfoMapContainer(builtEntityMap)); 28 | } 29 | 30 | return instance.get(); 31 | } 32 | 33 | const EntityInfoMap& EntityInfoMapContainer::getIpmiEntityRecords() 34 | { 35 | return entityRecords; 36 | } 37 | 38 | EntityInfoMap buildEntityMapFromFile() 39 | { 40 | const char* entityMapJsonFilename = 41 | "/usr/share/ipmi-providers/entity-map.json"; 42 | EntityInfoMap builtMap; 43 | 44 | std::ifstream mapFile(entityMapJsonFilename); 45 | if (!mapFile.is_open()) 46 | { 47 | return builtMap; 48 | } 49 | 50 | auto data = nlohmann::json::parse(mapFile, nullptr, false); 51 | if (data.is_discarded()) 52 | { 53 | return builtMap; 54 | } 55 | 56 | return buildJsonEntityMap(data); 57 | } 58 | 59 | EntityInfoMap buildJsonEntityMap(const nlohmann::json& data) 60 | { 61 | EntityInfoMap builtMap; 62 | 63 | if (data.type() != nlohmann::json::value_t::array) 64 | { 65 | return builtMap; 66 | } 67 | 68 | try 69 | { 70 | for (const auto& entry : data) 71 | { 72 | /* It's an array entry with the following fields: id, 73 | * containerEntityId, containerEntityInstance, isList, isLinked, 74 | * entities[4] 75 | */ 76 | EntityInfo obj; 77 | Id recordId = entry.at("id").get(); 78 | obj.containerEntityId = 79 | entry.at("containerEntityId").get(); 80 | obj.containerEntityInstance = 81 | entry.at("containerEntityInstance").get(); 82 | obj.isList = entry.at("isList").get(); 83 | obj.isLinked = entry.at("isLinked").get(); 84 | 85 | auto jsonEntities = entry.at("entities"); 86 | 87 | if (jsonEntities.type() != nlohmann::json::value_t::array) 88 | { 89 | throw std::runtime_error( 90 | "Invalid type for entities entry, must be array"); 91 | } 92 | if (jsonEntities.size() != obj.containedEntities.size()) 93 | { 94 | throw std::runtime_error( 95 | "Entities must be in pairs of " + 96 | std::to_string(obj.containedEntities.size())); 97 | } 98 | 99 | for (std::size_t i = 0; i < obj.containedEntities.size(); i++) 100 | { 101 | obj.containedEntities[i] = std::make_pair( 102 | jsonEntities[i].at("id").get(), 103 | jsonEntities[i].at("instance").get()); 104 | } 105 | 106 | builtMap.insert({recordId, obj}); 107 | } 108 | } 109 | catch (const std::exception& e) 110 | { 111 | /* If any entry is invalid, the entire file cannot be trusted. */ 112 | builtMap.clear(); 113 | } 114 | 115 | return builtMap; 116 | } 117 | 118 | } // namespace sensor 119 | } // namespace ipmi 120 | -------------------------------------------------------------------------------- /libipmid/meson.build: -------------------------------------------------------------------------------- 1 | ipmid_pre = [ 2 | boost, 3 | libsystemd_dep, 4 | phosphor_dbus_interfaces_dep, 5 | phosphor_logging_dep, 6 | sdbusplus_dep, 7 | ] 8 | 9 | entity_map_json_lib = static_library( 10 | 'entity_map_json', 11 | 'entity_map_json.cpp', 12 | include_directories: root_inc, 13 | dependencies: [nlohmann_json_dep, sdbusplus_dep], 14 | implicit_include_directories: false, 15 | ) 16 | 17 | entity_map_json_dep = declare_dependency(link_whole: entity_map_json_lib) 18 | 19 | libipmid = library( 20 | 'ipmid', 21 | 'sdbus-asio.cpp', 22 | 'signals.cpp', 23 | 'systemintf-sdbus.cpp', 24 | 'utils.cpp', 25 | dependencies: [ipmid_pre, entity_map_json_dep], 26 | version: meson.project_version(), 27 | include_directories: root_inc, 28 | install: true, 29 | install_dir: get_option('libdir'), 30 | override_options: ['b_lundef=false'], 31 | ) 32 | 33 | ipmid_dep = declare_dependency( 34 | dependencies: ipmid_pre, 35 | include_directories: root_inc, 36 | link_with: libipmid, 37 | ) 38 | 39 | import('pkgconfig').generate( 40 | libipmid, 41 | name: 'libipmid', 42 | version: meson.project_version(), 43 | description: 'ipmid', 44 | ) 45 | -------------------------------------------------------------------------------- /libipmid/sdbus-asio.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | namespace 7 | { 8 | 9 | std::shared_ptr ioCtx; 10 | std::shared_ptr sdbusp; 11 | 12 | } // namespace 13 | 14 | void setIoContext(std::shared_ptr& newIo) 15 | { 16 | ioCtx = newIo; 17 | } 18 | 19 | std::shared_ptr getIoContext() 20 | { 21 | return ioCtx; 22 | } 23 | 24 | void setSdBus(std::shared_ptr& newBus) 25 | { 26 | sdbusp = newBus; 27 | } 28 | 29 | std::shared_ptr getSdBus() 30 | { 31 | return sdbusp; 32 | } 33 | -------------------------------------------------------------------------------- /libipmid/signals.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace 10 | { 11 | 12 | class SignalHandler 13 | { 14 | public: 15 | SignalHandler(std::shared_ptr& io, int sigNum) : 16 | signal(std::make_unique(*io, sigNum)) 17 | { 18 | asyncWait(); 19 | } 20 | 21 | ~SignalHandler() 22 | { 23 | // unregister with asio to unmask the signal 24 | signal->cancel(); 25 | signal->clear(); 26 | } 27 | 28 | void registerHandler(int prio, 29 | const std::function& handler) 30 | { 31 | // check for initial placement 32 | if (handlers.empty() || std::get<0>(handlers.front()) < prio) 33 | { 34 | handlers.emplace_front(std::make_tuple(prio, handler)); 35 | return; 36 | } 37 | // walk the list and put it in the right place 38 | auto j = handlers.begin(); 39 | for (auto i = j; i != handlers.end() && std::get<0>(*i) > prio; i++) 40 | { 41 | j = i; 42 | } 43 | handlers.emplace_after(j, std::make_tuple(prio, handler)); 44 | } 45 | 46 | void handleSignal(const boost::system::error_code& ec, int sigNum) 47 | { 48 | if (ec) 49 | { 50 | lg2::error("Error in common signal handler, " 51 | "signal: {SIGNAL}, error: {ERROR}", 52 | "SIGNAL", sigNum, "ERROR", ec.message()); 53 | return; 54 | } 55 | for (auto h = handlers.begin(); h != handlers.end(); h++) 56 | { 57 | std::function& handler = std::get<1>(*h); 58 | if (handler(sigNum) == SignalResponse::breakExecution) 59 | { 60 | break; 61 | } 62 | } 63 | // start the wait for the next signal 64 | asyncWait(); 65 | } 66 | 67 | protected: 68 | void asyncWait() 69 | { 70 | signal->async_wait([this](const boost::system::error_code& ec, 71 | int sigNum) { handleSignal(ec, sigNum); }); 72 | } 73 | 74 | std::forward_list>> 75 | handlers; 76 | std::unique_ptr signal; 77 | }; 78 | 79 | // SIGRTMAX is defined as a non-constexpr function call and thus cannot be used 80 | // as an array size. Get around this by making a vector and resizing it the 81 | // first time it is needed 82 | std::vector> signals; 83 | 84 | } // namespace 85 | 86 | void registerSignalHandler(int priority, int signalNumber, 87 | const std::function& handler) 88 | { 89 | if (signalNumber >= SIGRTMAX) 90 | { 91 | return; 92 | } 93 | 94 | if (signals.empty()) 95 | { 96 | signals.resize(SIGRTMAX); 97 | } 98 | 99 | if (!signals[signalNumber]) 100 | { 101 | std::shared_ptr io = getIoContext(); 102 | signals[signalNumber] = 103 | std::make_unique(io, signalNumber); 104 | } 105 | signals[signalNumber]->registerHandler(priority, handler); 106 | } 107 | -------------------------------------------------------------------------------- /libipmid/systemintf-sdbus.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | namespace 7 | { 8 | 9 | std::unique_ptr sdbusp; 10 | 11 | } // namespace 12 | 13 | /** 14 | * @brief ipmid_get_sdbus_plus_handler is used by some ipmi providers 15 | * 16 | * @return: a reference to a unique pointer of the systemd connection 17 | * managed by the systemintfcmds code 18 | */ 19 | std::unique_ptr& ipmid_get_sdbus_plus_handler() 20 | { 21 | if (!sdbusp) 22 | { 23 | // Create a new sdbus connection so it can have a well-known name 24 | sd_bus* bus = nullptr; 25 | sd_bus_open_system(&bus); 26 | if (bus) 27 | { 28 | sdbusp = std::make_unique( 29 | *getIoContext(), bus); 30 | } 31 | } 32 | return sdbusp; 33 | } 34 | -------------------------------------------------------------------------------- /oem/example/meson.build: -------------------------------------------------------------------------------- 1 | # example OEM library build 2 | 3 | example_oem_src = ['apphandler.cpp'] 4 | 5 | example_oem_deps = [ 6 | boost, 7 | crypto, 8 | ipmid_dep, 9 | nlohmann_json_dep, 10 | phosphor_dbus_interfaces_dep, 11 | phosphor_logging_dep, 12 | sdbusplus_dep, 13 | stdplus_dep, 14 | ] 15 | 16 | 17 | example_oem_lib = library( 18 | 'example-oem', 19 | example_oem_src, 20 | dependencies: example_oem_deps, 21 | include_directories: [root_inc], 22 | install: true, 23 | install_dir: get_option('libdir') / 'ipmid-providers', 24 | version: meson.project_version(), 25 | override_options: ipmi_plugin_options, 26 | ) 27 | -------------------------------------------------------------------------------- /oem/meson.build: -------------------------------------------------------------------------------- 1 | oem_opt = get_option('oem-libraries') 2 | 3 | ipmi_plugin_options = ['b_lundef=false'] 4 | 5 | ################################################# 6 | # keep options below sorted alphabetically 7 | ################################################# 8 | if 'example' in oem_opt or 'all' in oem_opt 9 | subdir('example') 10 | endif 11 | -------------------------------------------------------------------------------- /read_fru_data.cpp: -------------------------------------------------------------------------------- 1 | #include "read_fru_data.hpp" 2 | 3 | #include "fruread.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | extern const FruMap frus; 17 | namespace ipmi 18 | { 19 | namespace fru 20 | { 21 | 22 | using namespace phosphor::logging; 23 | using InternalFailure = 24 | sdbusplus::error::xyz::openbmc_project::common::InternalFailure; 25 | std::unique_ptr matchPtr 26 | __attribute__((init_priority(101))); 27 | 28 | namespace cache 29 | { 30 | // User initiate read FRU info area command followed by 31 | // FRU read command. Also data is read in small chunks of 32 | // the specified offset and count. 33 | // Caching the data which will be invalidated when ever there 34 | // is a change in FRU properties. 35 | FRUAreaMap fruMap; 36 | } // namespace cache 37 | /** 38 | * @brief Read all the property value's for the specified interface 39 | * from Inventory. 40 | * 41 | * @param[in] intf Interface 42 | * @param[in] path Object path 43 | * @return map of properties 44 | */ 45 | ipmi::PropertyMap readAllProperties(const std::string& intf, 46 | const std::string& path) 47 | { 48 | ipmi::PropertyMap properties; 49 | sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 50 | std::string service; 51 | std::string objPath; 52 | 53 | // Is the path the full dbus path? 54 | if (path.find(xyzPrefix) != std::string::npos) 55 | { 56 | service = ipmi::getService(bus, intf, path); 57 | objPath = path; 58 | } 59 | else 60 | { 61 | service = ipmi::getService(bus, invMgrInterface, invObjPath); 62 | objPath = invObjPath + path; 63 | } 64 | 65 | auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 66 | propInterface, "GetAll"); 67 | method.append(intf); 68 | try 69 | { 70 | auto reply = bus.call(method); 71 | reply.read(properties); 72 | } 73 | catch (const sdbusplus::exception_t& e) 74 | { 75 | // If property is not found simply return empty value 76 | lg2::error("Error in reading property values: {ERROR}, path: {PATH}, " 77 | "interface: {INTERFACE}", 78 | "ERROR", e, "PATH", objPath, "INTERFACE", intf); 79 | } 80 | 81 | return properties; 82 | } 83 | 84 | void processFruPropChange(sdbusplus::message_t& msg) 85 | { 86 | if (cache::fruMap.empty()) 87 | { 88 | return; 89 | } 90 | std::string path = msg.get_path(); 91 | // trim the object base path, if found at the beginning 92 | if (path.compare(0, strlen(invObjPath), invObjPath) == 0) 93 | { 94 | path.erase(0, strlen(invObjPath)); 95 | } 96 | for (const auto& [fruId, instanceList] : frus) 97 | { 98 | auto found = std::find_if( 99 | instanceList.begin(), instanceList.end(), 100 | [&path](const auto& iter) { return (iter.path == path); }); 101 | 102 | if (found != instanceList.end()) 103 | { 104 | cache::fruMap.erase(fruId); 105 | break; 106 | } 107 | } 108 | } 109 | 110 | // register for fru property change 111 | int registerCallbackHandler() 112 | { 113 | if (matchPtr == nullptr) 114 | { 115 | using namespace sdbusplus::bus::match::rules; 116 | sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 117 | matchPtr = std::make_unique( 118 | bus, 119 | path_namespace(invObjPath) + type::signal() + 120 | member("PropertiesChanged") + interface(propInterface), 121 | std::bind(processFruPropChange, std::placeholders::_1)); 122 | } 123 | return 0; 124 | } 125 | 126 | /** 127 | * @brief Read FRU property values from Inventory 128 | * 129 | * @param[in] fruNum FRU id 130 | * @return populate FRU Inventory data 131 | */ 132 | FruInventoryData readDataFromInventory(const FRUId& fruNum) 133 | { 134 | auto iter = frus.find(fruNum); 135 | if (iter == frus.end()) 136 | { 137 | lg2::error("Unsupported FRU ID: {FRUID}", "FRUID", fruNum); 138 | elog(); 139 | } 140 | 141 | FruInventoryData data; 142 | auto& instanceList = iter->second; 143 | for (auto& instance : instanceList) 144 | { 145 | for (auto& intf : instance.interfaces) 146 | { 147 | ipmi::PropertyMap allProp = 148 | readAllProperties(intf.first, instance.path); 149 | for (auto& properties : intf.second) 150 | { 151 | auto iter = allProp.find(properties.first); 152 | if (iter != allProp.end()) 153 | { 154 | data[properties.second.section].emplace( 155 | properties.second.property, 156 | std::move( 157 | std::get(allProp[properties.first]))); 158 | } 159 | } 160 | } 161 | } 162 | return data; 163 | } 164 | 165 | const FruAreaData& getFruAreaData(const FRUId& fruNum) 166 | { 167 | auto iter = cache::fruMap.find(fruNum); 168 | if (iter != cache::fruMap.end()) 169 | { 170 | return iter->second; 171 | } 172 | auto invData = readDataFromInventory(fruNum); 173 | 174 | // Build area info based on inventory data 175 | FruAreaData newdata = buildFruAreaData(std::move(invData)); 176 | cache::fruMap.emplace(fruNum, std::move(newdata)); 177 | return cache::fruMap.at(fruNum); 178 | } 179 | } // namespace fru 180 | } // namespace ipmi 181 | -------------------------------------------------------------------------------- /read_fru_data.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ipmi_fru_info_area.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace ipmi 9 | { 10 | namespace fru 11 | { 12 | using FRUId = uint8_t; 13 | using FRUAreaMap = std::map; 14 | 15 | static constexpr auto xyzPrefix = "/xyz/openbmc_project/"; 16 | static constexpr auto invMgrInterface = "xyz.openbmc_project.Inventory.Manager"; 17 | static constexpr auto invObjPath = "/xyz/openbmc_project/inventory"; 18 | static constexpr auto propInterface = "org.freedesktop.DBus.Properties"; 19 | static constexpr auto invItemInterface = "xyz.openbmc_project.Inventory.Item"; 20 | static constexpr auto itemPresentProp = "Present"; 21 | 22 | /** 23 | * @brief Get fru area data as per IPMI specification 24 | * 25 | * @param[in] fruNum FRU ID 26 | * 27 | * @return FRU area data as per IPMI specification 28 | */ 29 | const FruAreaData& getFruAreaData(const FRUId& fruNum); 30 | 31 | /** 32 | * @brief Register callback handler into DBUS for PropertyChange events 33 | * 34 | * @return negative value on failure 35 | */ 36 | int registerCallbackHandler(); 37 | } // namespace fru 38 | } // namespace ipmi 39 | -------------------------------------------------------------------------------- /scripts/entity-example.md: -------------------------------------------------------------------------------- 1 | # Entity-example 2 | 3 | If your platform requires the entity container map, you can provide a json file 4 | of the format: 5 | 6 | ```json 7 | [ 8 | { 9 | "id": 1, 10 | "containerEntityId": 2, 11 | "containerEntityInstance": 3, 12 | "isList": false, 13 | "isLinked": false, 14 | "entities": [ 15 | { "id": 1, "instance": 2 }, 16 | { "id": 1, "instance": 3 }, 17 | { "id": 1, "instance": 4 }, 18 | { "id": 1, "instance": 5 } 19 | ] 20 | } 21 | ] 22 | ``` 23 | 24 | as part of your `phosphor-ipmi-config` 25 | 26 | The above json is identical to the original YAML documented below: 27 | 28 | ```text 29 | # This record has: 30 | # Container Entity Id and Container Entity Instance = (0x13, 0x81) 31 | # Contained Entity Id and Contained Entity Instance = (0x0A, 0x1), 32 | # (0x0A, 0x3), (0x0A, 0x5), (0x0A, 0x7) 33 | # Entity Record id is the key 34 | 0x01: 35 | # Container entity contains other entities 36 | # Entity Id and entity Instance for the container entity 37 | containerEntityId: 0x13 38 | containerEntityInstance: 0x81 39 | # A record can have contained entities as a four entry list or as upto 40 | # two ranges of entity instances; this record has contained entities 41 | # as a four entry list 42 | isList: "true" 43 | # Records can be linked if necessary to extend the number of contained 44 | # entities within a container entity; this record is not linked 45 | isLinked: "false" 46 | entityId1: 0x0A 47 | entityInstance1: 0x1 48 | entityId2: 0x0A 49 | entityInstance2: 0x3 50 | entityId3: 0x0A 51 | entityInstance3: 0x5 52 | entityId4: 0x0A 53 | entityInstance4: 0x7 54 | 55 | # The below two records have: 56 | # Container Entity Id and Container Entity Instance = (0x18, 0x2) 57 | # Contained Entity Id and Contained Entity Instance = (0x1D, 0x1), 58 | # (0x1D, 0x4), (0x1D, 0x6), (0x2B, 0x1), (0x2B, 0x3), (0x0F, 0x1), 59 | # (0x0F, 0x3), (0x10, 0x5) 60 | 0x02: 61 | containerEntityId: 0x18 62 | containerEntityInstance: 0x2 63 | # This record has contained entities as a four entry list 64 | isList: "true" 65 | # This record is linked with the below record; this record and the 66 | # below record have the same container entity Id and container entity 67 | # instance; 68 | isLinked: "true" 69 | entityId1: 0x1D 70 | entityInstance1: 0x1 71 | entityId2: 0x1D 72 | entityInstance2: 0x4 73 | entityId3: 0x1D 74 | entityInstance3: 0x6 75 | entityId4: 0x2B 76 | entityInstance4: 0x1 77 | 78 | 0x03: 79 | containerEntityId: 0x18 80 | containerEntityInstance: 0x2 81 | # This record has contained entities as a four entry list 82 | isList: "true" 83 | # This record is linked with the above record; this record and the 84 | # above record have the same container entity Id and container entity 85 | # instance 86 | isLinked: "true" 87 | entityId1: 0x2B 88 | entityInstance1: 0x3 89 | entityId2: 0x0F 90 | entityInstance2: 0x1 91 | entityId3: 0x0F 92 | entityInstance3: 0x3 93 | entityId4: 0x10 94 | entityInstance4: 0x5 95 | 96 | # This record has: 97 | # Container Entity Id and Container Entity Instance = (0x1E, 0x1) 98 | # Contained Entity Id and Contained Entity Instance = (0x20, 0x1), 99 | # (0x20, 0x2), (0x20, 0x3), (0x20, 0x7), (0x20, 0x8), (0x20, 0x9) 100 | 0x04: 101 | containerEntityId: 0x1E 102 | containerEntityInstance: 0x1 103 | # This record has contained entities as two ranges of entity instances 104 | isList: "false" 105 | # This record is not linked 106 | isLinked: "false" 107 | entityId1: 0x20 108 | entityInstance1: 0x1 109 | entityId2: 0x20 110 | entityInstance2: 0x3 111 | entityId3: 0x20 112 | entityInstance3: 0x7 113 | entityId4: 0x20 114 | entityInstance4: 0x9 115 | 116 | # The below two records have: 117 | # Container Entity Id and Container Entity Instance = (0x1E, 0x3) 118 | # Contained Entity Id and Contained Entity Instance = (0x20, 0x1), 119 | # (0x20, 0x2), (0x20, 0x3), (0x20, 0x6), (0x20, 0x7), (0x20, 0x8), 120 | # (0x20, 0xA), (0x20, 0xB), (0x20, 0xD), (0x20, 0xE), (0x20, 0xF) 121 | 0x05: 122 | containerEntityId: 0x1E 123 | containerEntityInstance: 0x03 124 | # This record has contained entities as two ranges of entity instances 125 | isList: "false" 126 | # This record is linked with the below record; this record and the 127 | # below record have the same container entity Id and container entity 128 | # instance; 129 | isLinked: "true" 130 | entityId1: 0x20 131 | entityInstance1: 0x1 132 | entityId2: 0x20 133 | entityInstance2: 0x3 134 | entityId3: 0x20 135 | entityInstance3: 0x6 136 | entityId4: 0x20 137 | entityInstance4: 0x8 138 | 139 | 0x06: 140 | containerEntityId: 0x1E 141 | containerEntityInstance: 0x03 142 | # This record has contained entities as two ranges of entity instances 143 | isList: "false" 144 | # This record is linked with the above record; this record and the 145 | # above record have the same container entity Id and container entity 146 | # instance; 147 | isLinked: "true" 148 | entityId1: 0x20 149 | entityInstance1: 0xA 150 | entityId2: 0x20 151 | entityInstance2: 0xB 152 | entityId3: 0x20 153 | entityInstance3: 0xD 154 | entityId4: 0x20 155 | entityInstance4: 0xF 156 | ``` 157 | -------------------------------------------------------------------------------- /scripts/fru-read-example.yaml: -------------------------------------------------------------------------------- 1 | # A YAML similar to this example would have to be generated, for eg with MRW 2 | # inputs and system configuration, to depict IPMI Fru information. 3 | # 4 | # This file maps IPMI properties to phosphor dbus inventory properties 5 | # 6 | # This YAML could help generate C++ code. 7 | # Format of the YAML: 8 | # Fruid: 9 | # Associated Fru paths 10 | # d-bus Interfaces 11 | # d-bus Properties 12 | # IPMI Fru mapping 13 | 0: 14 | /system: 15 | entityID: 23 16 | entityInstance: 1 17 | interfaces: 18 | xyz.openbmc_project.Inventory.Item: 19 | PrettyName: 20 | IPMIFruProperty: Product Name 21 | IPMIFruSection: Product 22 | xyz.openbmc_project.Inventory.Decorator.Asset: 23 | Manufacturer: 24 | IPMIFruProperty: Manufacturer 25 | IPMIFruSection: Product 26 | PartNumber: 27 | IPMIFruProperty: Part Number 28 | IPMIFruSection: Product 29 | SerialNumber: 30 | IPMIFruProperty: Serial Number 31 | IPMIFruSection: Product 32 | BuildDate: 33 | IPMIFruProperty: Mfg Date 34 | IPMIFruSection: Product 35 | xyz.openbmc_project.Inventory.Decorator.Revision: 36 | Version: 37 | IPMIFruProperty: Version 38 | IPMIFruSection: Product 39 | xyz.openbmc_project.Inventory.Item.System: 40 | 1: 41 | /system/chassis/motherboard/dimm0: 42 | entityID: 32 43 | entityInstance: 1 44 | interfaces: 45 | xyz.openbmc_project.Inventory.Item: 46 | PrettyName: 47 | IPMIFruProperty: Product Name 48 | IPMIFruSection: Product 49 | xyz.openbmc_project.Inventory.Decorator.Asset: 50 | Manufacturer: 51 | IPMIFruProperty: Manufacturer 52 | IPMIFruSection: Product 53 | BuildDate: 54 | IPMIFruProperty: Mfg Date 55 | IPMIFruSection: Product 56 | SerialNumber: 57 | IPMIFruProperty: Serial Number 58 | IPMIFruSection: Product 59 | PartNumber: 60 | IPMIFruProperty: Part Number 61 | IPMIFruSection: Product 62 | xyz.openbmc_project.Inventory.Decorator.Revision: 63 | Version: 64 | IPMIFruProperty: Version 65 | IPMIFruSection: Product 66 | xyz.openbmc_project.Inventory.Item.Dimm: 67 | 2: 68 | /system/chassis/motherboard/dimm1: 69 | entityID: 32 70 | entityInstance: 2 71 | interfaces: 72 | xyz.openbmc_project.Inventory.Item: 73 | PrettyName: 74 | IPMIFruProperty: Product Name 75 | IPMIFruSection: Product 76 | xyz.openbmc_project.Inventory.Decorator.Asset: 77 | Manufacturer: 78 | IPMIFruProperty: Manufacturer 79 | IPMIFruSection: Product 80 | BuildDate: 81 | IPMIFruProperty: Mfg Date 82 | IPMIFruSection: Product 83 | SerialNumber: 84 | IPMIFruProperty: Serial Number 85 | IPMIFruSection: Product 86 | PartNumber: 87 | IPMIFruProperty: Part Number 88 | IPMIFruSection: Product 89 | xyz.openbmc_project.Inventory.Decorator.Revision: 90 | Version: 91 | IPMIFruProperty: Version 92 | IPMIFruSection: Product 93 | xyz.openbmc_project.Inventory.Item.Dimm: 94 | 3: 95 | /system/chassis/motherboard/cpu0: 96 | entityID: 3 97 | entityInstance: 1 98 | interfaces: 99 | xyz.openbmc_project.Inventory.Item: 100 | PrettyName: 101 | IPMIFruProperty: Product Name 102 | IPMIFruSection: Board 103 | xyz.openbmc_project.Inventory.Decorator.Asset: 104 | BuildDate: 105 | IPMIFruProperty: Mfg Date 106 | IPMIFruSection: Board 107 | SerialNumber: 108 | IPMIFruProperty: Serial Number 109 | IPMIFruSection: Board 110 | PartNumber: 111 | IPMIFruProperty: Part Number 112 | IPMIFruSection: Board 113 | Manufacturer: 114 | IPMIFruProperty: Manufacturer 115 | IPMIFruSection: Board 116 | xyz.openbmc_project.Inventory.Item.Cpu: 117 | 4: 118 | /system/chassis/motherboard/cpu1: 119 | entityID: 3 120 | entityInstance: 2 121 | interfaces: 122 | xyz.openbmc_project.Inventory.Item: 123 | PrettyName: 124 | IPMIFruProperty: Product Name 125 | IPMIFruSection: Board 126 | xyz.openbmc_project.Inventory.Decorator.Asset: 127 | BuildDate: 128 | IPMIFruProperty: Mfg Date 129 | IPMIFruSection: Board 130 | SerialNumber: 131 | IPMIFruProperty: Serial Number 132 | IPMIFruSection: Board 133 | PartNumber: 134 | IPMIFruProperty: Part Number 135 | IPMIFruSection: Board 136 | Manufacturer: 137 | IPMIFruProperty: Manufacturer 138 | IPMIFruSection: Board 139 | xyz.openbmc_project.Inventory.Item.Cpu: 140 | -------------------------------------------------------------------------------- /scripts/fru_gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import os 5 | import sys 6 | 7 | import yaml 8 | from mako.template import Template 9 | 10 | 11 | def generate_cpp(inventory_yaml, output_dir): 12 | with open(inventory_yaml, "r") as f: 13 | ifile = yaml.safe_load(f) 14 | if not isinstance(ifile, dict): 15 | ifile = {} 16 | 17 | # Render the mako template 18 | 19 | t = Template(filename=os.path.join(script_dir, "readfru.cpp.mako")) 20 | 21 | output_hpp = os.path.join(output_dir, "fru-read-gen.cpp") 22 | with open(output_hpp, "w") as fd: 23 | fd.write(t.render(fruDict=ifile)) 24 | 25 | 26 | def main(): 27 | valid_commands = {"generate-cpp": generate_cpp} 28 | parser = argparse.ArgumentParser(description="IPMI FRU map code generator") 29 | 30 | parser.add_argument( 31 | "-i", 32 | "--inventory_yaml", 33 | dest="inventory_yaml", 34 | default="example.yaml", 35 | help="input inventory yaml file to parse", 36 | ) 37 | 38 | parser.add_argument( 39 | "-o", 40 | "--output-dir", 41 | dest="outputdir", 42 | default=".", 43 | help="output directory", 44 | ) 45 | 46 | parser.add_argument( 47 | "command", 48 | metavar="COMMAND", 49 | type=str, 50 | choices=valid_commands.keys(), 51 | help="Command to run.", 52 | ) 53 | 54 | args = parser.parse_args() 55 | 56 | if not (os.path.isfile(args.inventory_yaml)): 57 | sys.exit("Can not find input yaml file " + args.inventory_yaml) 58 | 59 | function = valid_commands[args.command] 60 | function(args.inventory_yaml, args.outputdir) 61 | 62 | 63 | if __name__ == "__main__": 64 | script_dir = os.path.dirname(os.path.realpath(__file__)) 65 | main() 66 | -------------------------------------------------------------------------------- /scripts/inventory-sensor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import os 5 | import sys 6 | 7 | import yaml 8 | from mako.template import Template 9 | 10 | 11 | def generate_cpp(sensor_yaml, output_dir): 12 | with open(sensor_yaml, "r") as f: 13 | ifile = yaml.safe_load(f) 14 | if not isinstance(ifile, dict): 15 | ifile = {} 16 | 17 | # Render the mako template 18 | 19 | t = Template( 20 | filename=os.path.join(script_dir, "inventorysensor.cpp.mako") 21 | ) 22 | 23 | output_cpp = os.path.join(output_dir, "inventory-sensor-gen.cpp") 24 | with open(output_cpp, "w") as fd: 25 | fd.write(t.render(sensorDict=ifile)) 26 | 27 | 28 | def main(): 29 | valid_commands = {"generate-cpp": generate_cpp} 30 | parser = argparse.ArgumentParser( 31 | description="Inventory Object to IPMI SensorID code generator" 32 | ) 33 | 34 | parser.add_argument( 35 | "-i", 36 | "--sensor_yaml", 37 | dest="sensor_yaml", 38 | default="example.yaml", 39 | help="input sensor yaml file to parse", 40 | ) 41 | 42 | parser.add_argument( 43 | "-o", 44 | "--output-dir", 45 | dest="outputdir", 46 | default=".", 47 | help="output directory", 48 | ) 49 | 50 | parser.add_argument( 51 | "command", 52 | metavar="COMMAND", 53 | type=str, 54 | choices=valid_commands.keys(), 55 | help="Command to run.", 56 | ) 57 | 58 | args = parser.parse_args() 59 | 60 | if not (os.path.isfile(args.sensor_yaml)): 61 | sys.exit("Can not find input yaml file " + args.sensor_yaml) 62 | 63 | function = valid_commands[args.command] 64 | function(args.sensor_yaml, args.outputdir) 65 | 66 | 67 | if __name__ == "__main__": 68 | script_dir = os.path.dirname(os.path.realpath(__file__)) 69 | main() 70 | -------------------------------------------------------------------------------- /scripts/inventorysensor.cpp.mako: -------------------------------------------------------------------------------- 1 | ## This file is a template. The comment below is emitted 2 | ## into the rendered file; feel free to edit this file. 3 | 4 | // !!! WARNING: This is a GENERATED Code..Please do NOT Edit !!! 5 | 6 | #include 7 | using namespace ipmi::sensor; 8 | 9 | extern const InvObjectIDMap __attribute__((init_priority(101))) invSensors = { 10 | % for key in sensorDict.keys(): 11 | % if key: 12 | {"${key}", 13 | { 14 | <% 15 | objectPath = sensorDict[key] 16 | sensorID = objectPath["sensorID"] 17 | sensorType = objectPath["sensorType"] 18 | eventReadingType = objectPath["eventReadingType"] 19 | offset = objectPath["offset"] 20 | %> 21 | ${sensorID},${sensorType},${eventReadingType},${offset} 22 | } 23 | }, 24 | % endif 25 | % endfor 26 | }; 27 | -------------------------------------------------------------------------------- /scripts/meson.build: -------------------------------------------------------------------------------- 1 | # Generate Configuration Files from Yaml 2 | python_exe = find_program('python3', 'python') 3 | 4 | sensor_gen = custom_target( 5 | 'sensor-gen', 6 | output: 'sensor-gen.cpp', 7 | input: ['sensor_gen.py', get_option('sensor-yaml-gen')], 8 | command: [ 9 | python_exe, 10 | '@INPUT0@', 11 | '-i', 12 | '@INPUT1@', 13 | '-o', 14 | meson.current_build_dir(), 15 | 'generate-cpp', 16 | ], 17 | ) 18 | generated_src += sensor_gen 19 | 20 | invsensor_gen = custom_target( 21 | 'invsensor-gen', 22 | output: 'inventory-sensor-gen.cpp', 23 | input: ['inventory-sensor.py', get_option('invsensor-yaml-gen')], 24 | command: [ 25 | python_exe, 26 | '@INPUT0@', 27 | '-i', 28 | '@INPUT1@', 29 | '-o', 30 | meson.current_build_dir(), 31 | 'generate-cpp', 32 | ], 33 | ) 34 | generated_src += invsensor_gen 35 | 36 | fru_gen = custom_target( 37 | 'fru-gen', 38 | output: 'fru-read-gen.cpp', 39 | input: ['fru_gen.py', get_option('fru-yaml-gen')], 40 | command: [ 41 | python_exe, 42 | '@INPUT0@', 43 | '-i', 44 | '@INPUT1@', 45 | '-o', 46 | meson.current_build_dir(), 47 | 'generate-cpp', 48 | ], 49 | ) 50 | generated_src += fru_gen 51 | -------------------------------------------------------------------------------- /scripts/readfru.cpp.mako: -------------------------------------------------------------------------------- 1 | // !!! WARNING: This is a GENERATED Code..Please do NOT Edit !!! 2 | #include 3 | #include "fruread.hpp" 4 | 5 | extern const FruMap __attribute__((init_priority(101))) frus = { 6 | % for key in fruDict.keys(): 7 | {${key},{ 8 | <% 9 | instanceList = fruDict[key] 10 | %> 11 | % for instancePath,instanceInfo in instanceList.items(): 12 | <% 13 | entityID = instanceInfo["entityID"] 14 | entityInstance = instanceInfo["entityInstance"] 15 | interfaces = instanceInfo["interfaces"] 16 | %> 17 | {${entityID}, ${entityInstance}, "${instancePath}",{ 18 | % for interface,properties in interfaces.items(): 19 | {"${interface}",{ 20 | % if properties: 21 | % for dbus_property,property_value in properties.items(): 22 | {"${dbus_property}",{ 23 | "${property_value.get("IPMIFruSection", "")}", 24 | "${property_value.get("IPMIFruProperty", "")}",\ 25 | <% 26 | delimiter = property_value.get("IPMIFruValueDelimiter") 27 | if not delimiter: 28 | delimiter = "" 29 | else: 30 | delimiter = '\\' + hex(delimiter)[1:] 31 | %> 32 | "${delimiter}" 33 | }}, 34 | % endfor 35 | %endif 36 | }}, 37 | % endfor 38 | }}, 39 | % endfor 40 | }}, 41 | % endfor 42 | }; 43 | -------------------------------------------------------------------------------- /scripts/sensor_gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import os 5 | import sys 6 | 7 | import yaml 8 | from mako.template import Template 9 | 10 | 11 | def generate_cpp(sensor_yaml, output_dir): 12 | with open(sensor_yaml, "r") as f: 13 | ifile = yaml.safe_load(f) 14 | if not isinstance(ifile, dict): 15 | ifile = {} 16 | 17 | # Render the mako template 18 | 19 | t = Template(filename=os.path.join(script_dir, "writesensor.cpp.mako")) 20 | 21 | output_cpp = os.path.join(output_dir, "sensor-gen.cpp") 22 | with open(output_cpp, "w") as fd: 23 | fd.write(t.render(sensorDict=ifile)) 24 | 25 | 26 | def main(): 27 | valid_commands = {"generate-cpp": generate_cpp} 28 | parser = argparse.ArgumentParser( 29 | description="IPMI Sensor parser and code generator" 30 | ) 31 | 32 | parser.add_argument( 33 | "-i", 34 | "--sensor_yaml", 35 | dest="sensor_yaml", 36 | default="example.yaml", 37 | help="input sensor yaml file to parse", 38 | ) 39 | 40 | parser.add_argument( 41 | "-o", 42 | "--output-dir", 43 | dest="outputdir", 44 | default=".", 45 | help="output directory", 46 | ) 47 | 48 | parser.add_argument( 49 | "command", 50 | metavar="COMMAND", 51 | type=str, 52 | choices=valid_commands.keys(), 53 | help="Command to run.", 54 | ) 55 | 56 | args = parser.parse_args() 57 | 58 | if not (os.path.isfile(args.sensor_yaml)): 59 | sys.exit("Can not find input yaml file " + args.sensor_yaml) 60 | 61 | function = valid_commands[args.command] 62 | function(args.sensor_yaml, args.outputdir) 63 | 64 | 65 | if __name__ == "__main__": 66 | script_dir = os.path.dirname(os.path.realpath(__file__)) 67 | main() 68 | -------------------------------------------------------------------------------- /settings.cpp: -------------------------------------------------------------------------------- 1 | #include "settings.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace settings 10 | { 11 | 12 | using namespace phosphor::logging; 13 | using namespace sdbusplus::error::xyz::openbmc_project::common; 14 | 15 | constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper"; 16 | constexpr auto mapperPath = "/xyz/openbmc_project/object_mapper"; 17 | constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper"; 18 | 19 | Objects::Objects(sdbusplus::bus_t& bus, const std::vector& filter) : 20 | bus(bus) 21 | { 22 | ipmi::ObjectTree objectTree; 23 | try 24 | { 25 | objectTree = ipmi::getSubTree(bus, filter); 26 | } 27 | catch (const std::exception& e) 28 | { 29 | lg2::error("Failed to call the getSubTree method: {ERROR}", "ERROR", e); 30 | elog(); 31 | } 32 | 33 | for (auto& iter : objectTree) 34 | { 35 | const auto& path = iter.first; 36 | for (auto& interface : iter.second.begin()->second) 37 | { 38 | auto found = map.find(interface); 39 | if (map.end() != found) 40 | { 41 | auto& paths = found->second; 42 | paths.push_back(path); 43 | } 44 | else 45 | { 46 | map.emplace(std::move(interface), std::vector({path})); 47 | } 48 | } 49 | } 50 | } 51 | 52 | Service Objects::service(const Path& path, const Interface& interface) const 53 | { 54 | using Interfaces = std::vector; 55 | auto mapperCall = 56 | bus.new_method_call(mapperService, mapperPath, mapperIntf, "GetObject"); 57 | mapperCall.append(path); 58 | mapperCall.append(Interfaces({interface})); 59 | 60 | std::map result; 61 | try 62 | { 63 | auto response = bus.call(mapperCall); 64 | response.read(result); 65 | return result.begin()->first; 66 | } 67 | catch (const std::exception& e) 68 | { 69 | lg2::error("Invalid response from mapper: {ERROR}", "ERROR", e); 70 | elog(); 71 | } 72 | } 73 | 74 | } // namespace settings 75 | -------------------------------------------------------------------------------- /settings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace settings 9 | { 10 | 11 | using Path = std::string; 12 | using Service = std::string; 13 | using Interface = std::string; 14 | 15 | /** @class Objects 16 | * @brief Fetch paths of settings d-bus objects of interest, upon construction 17 | */ 18 | struct Objects 19 | { 20 | public: 21 | /** @brief Constructor - fetch settings objects 22 | * 23 | * @param[in] bus - The Dbus bus object 24 | * @param[in] filter - A vector of settings interfaces the caller is 25 | * interested in. 26 | */ 27 | Objects(sdbusplus::bus_t& bus, const std::vector& filter); 28 | Objects(const Objects&) = default; 29 | Objects& operator=(const Objects&) = delete; 30 | Objects(Objects&&) = delete; 31 | Objects& operator=(Objects&&) = delete; 32 | ~Objects() = default; 33 | 34 | /** @brief Fetch d-bus service, given a path and an interface. The 35 | * service can't be cached because mapper returns unique 36 | * service names. 37 | * 38 | * @param[in] path - The Dbus object 39 | * @param[in] interface - The Dbus interface 40 | * 41 | * @return std::string - the dbus service 42 | */ 43 | Service service(const Path& path, const Interface& interface) const; 44 | 45 | /** @brief map of settings objects */ 46 | std::map> map; 47 | 48 | /** @brief The Dbus bus object */ 49 | sdbusplus::bus_t& bus; 50 | }; 51 | 52 | } // namespace settings 53 | -------------------------------------------------------------------------------- /softoff/mainapp.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © 2016 IBM Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include "config.h" 17 | 18 | #include "softoff.hpp" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // Return -1 on any errors to ensure we follow the calling targets OnFailure= 28 | // path 29 | int main(int, char**) 30 | { 31 | using namespace phosphor::logging; 32 | 33 | // Get a handle to system dbus. 34 | auto bus = sdbusplus::bus::new_default(); 35 | 36 | // Add systemd object manager. 37 | sdbusplus::server::manager_t(bus, SOFTOFF_OBJPATH); 38 | 39 | // Get default event loop 40 | auto event = sdeventplus::Event::get_default(); 41 | 42 | // Attach the bus to sd_event to service user requests 43 | bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL); 44 | 45 | // Claim the bus. Delaying it until sending SMS_ATN may result 46 | // in a race condition between this available and IPMI trying to send 47 | // message as a response to ack from host. 48 | bus.request_name(SOFTOFF_BUSNAME); 49 | 50 | // Create the SoftPowerOff object. 51 | phosphor::ipmi::SoftPowerOff powerObj(bus, event.get(), SOFTOFF_OBJPATH); 52 | 53 | // Wait for client requests until this application has processed 54 | // at least one successful SoftPowerOff or we timed out 55 | while (!powerObj.isCompleted() && !powerObj.isTimerExpired()) 56 | { 57 | try 58 | { 59 | event.run(std::nullopt); 60 | } 61 | catch (const sdeventplus::SdEventError& e) 62 | { 63 | lg2::error("Failure in processing request: {ERROR}", "ERROR", e); 64 | return 1; 65 | } 66 | } 67 | 68 | // Log an error if we timed out after getting Ack for SMS_ATN and before 69 | // getting the Host Shutdown response 70 | if (powerObj.isTimerExpired() && 71 | (powerObj.responseReceived() == 72 | phosphor::ipmi::Base::SoftPowerOff::HostResponse::SoftOffReceived)) 73 | { 74 | using error = 75 | sdbusplus::error::xyz::openbmc_project::state::host::SoftOffTimeout; 76 | using errorMetadata = xyz::openbmc_project::state::host::SoftOffTimeout; 77 | report(prev_entry()); 78 | return -1; 79 | } 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /softoff/meson.build: -------------------------------------------------------------------------------- 1 | softpower_pre = [ 2 | boost, 3 | ipmid_dep, 4 | libsystemd_dep, 5 | phosphor_dbus_interfaces_dep, 6 | phosphor_logging_dep, 7 | sdbusplus_dep, 8 | sdeventplus_dep, 9 | softoff_dbus, 10 | ] 11 | 12 | softpower_lib = static_library( 13 | 'softpower_lib', 14 | 'softoff.cpp', 15 | conf_h, 16 | dependencies: softpower_pre, 17 | include_directories: root_inc, 18 | ) 19 | 20 | softpower_dep = declare_dependency( 21 | dependencies: softpower_pre, 22 | include_directories: root_inc, 23 | link_with: softpower_lib, 24 | ) 25 | 26 | executable( 27 | 'phosphor-softpoweroff', 28 | 'mainapp.cpp', 29 | implicit_include_directories: false, 30 | dependencies: softpower_dep, 31 | include_directories: root_inc, 32 | install: true, 33 | install_dir: get_option('bindir'), 34 | ) 35 | -------------------------------------------------------------------------------- /softoff/softoff.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © 2016 IBM Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include "config.h" 17 | 18 | #include "softoff.hpp" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | namespace phosphor 26 | { 27 | namespace ipmi 28 | { 29 | 30 | using namespace sdbusplus::server::xyz::openbmc_project::control; 31 | 32 | void SoftPowerOff::sendHostShutDownCmd() 33 | { 34 | auto ctrlHostPath = 35 | std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0'; 36 | auto host = ::ipmi::getService(this->bus, CONTROL_HOST_BUSNAME, 37 | ctrlHostPath.c_str()); 38 | 39 | auto method = bus.new_method_call(host.c_str(), ctrlHostPath.c_str(), 40 | CONTROL_HOST_BUSNAME, "Execute"); 41 | 42 | method.append(convertForMessage(Host::Command::SoftOff).c_str()); 43 | try 44 | { 45 | auto reply = bus.call(method); 46 | } 47 | catch (const std::exception& e) 48 | { 49 | lg2::error("Error in call to control host Execute: {ERROR}", "ERROR", 50 | e); 51 | // TODO openbmc/openbmc#851 - Once available, throw returned error 52 | throw std::runtime_error("Error in call to control host Execute"); 53 | } 54 | } 55 | 56 | // Function called on host control signals 57 | void SoftPowerOff::hostControlEvent(sdbusplus::message_t& msg) 58 | { 59 | std::string cmdCompleted{}; 60 | std::string cmdStatus{}; 61 | 62 | msg.read(cmdCompleted, cmdStatus); 63 | 64 | lg2::debug( 65 | "Host control signal values, command: {COMMAND}, status:{STATUS}", 66 | "COMMAND", cmdCompleted, "STATUS", cmdStatus); 67 | 68 | if (Host::convertResultFromString(cmdStatus) == Host::Result::Success) 69 | { 70 | // Set our internal property indicating we got host attention 71 | sdbusplus::server::xyz::openbmc_project::ipmi::internal::SoftPowerOff:: 72 | responseReceived(HostResponse::SoftOffReceived); 73 | 74 | // Start timer for host shutdown 75 | using namespace std::chrono; 76 | auto time = duration_cast( 77 | seconds(IPMI_HOST_SHUTDOWN_COMPLETE_TIMEOUT_SECS)); 78 | auto r = startTimer(time); 79 | if (r < 0) 80 | { 81 | lg2::error( 82 | "Failure to start Host shutdown wait timer, ERRNO: {ERRNO}", 83 | "ERRNO", lg2::hex, -r); 84 | } 85 | else 86 | { 87 | lg2::info("Timer started waiting for host to shutdown, " 88 | "TIMEOUT_IN_MSEC: {TIMEOUT_IN_MSEC}", 89 | "TIMEOUT_IN_MSEC", 90 | (duration_cast( 91 | seconds(IPMI_HOST_SHUTDOWN_COMPLETE_TIMEOUT_SECS))) 92 | .count()); 93 | } 94 | } 95 | else 96 | { 97 | // An error on the initial attention is not considered an error, just 98 | // exit normally and allow remaining shutdown targets to run 99 | lg2::info("Timeout on host attention, continue with power down"); 100 | completed = true; 101 | } 102 | return; 103 | } 104 | 105 | // Starts a timer 106 | int SoftPowerOff::startTimer(const std::chrono::microseconds& usec) 107 | { 108 | return timer.start(usec); 109 | } 110 | 111 | // Host Response handler 112 | auto SoftPowerOff::responseReceived(HostResponse response) -> HostResponse 113 | { 114 | using namespace std::chrono; 115 | 116 | if (response == HostResponse::HostShutdown) 117 | { 118 | // Disable the timer since Host has quiesced and we are 119 | // done with soft power off part 120 | auto r = timer.stop(); 121 | if (r < 0) 122 | { 123 | lg2::error("Failure to STOP the timer, ERRNO: {ERRNO}", "ERRNO", 124 | lg2::hex, -r); 125 | } 126 | 127 | // This marks the completion of soft power off sequence. 128 | completed = true; 129 | } 130 | 131 | return sdbusplus::server::xyz::openbmc_project::ipmi::internal:: 132 | SoftPowerOff::responseReceived(response); 133 | } 134 | 135 | } // namespace ipmi 136 | } // namespace phosphor 137 | -------------------------------------------------------------------------------- /softoff/softoff.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "config.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | namespace phosphor 13 | { 14 | namespace ipmi 15 | { 16 | 17 | namespace Base = sdbusplus::server::xyz::openbmc_project::ipmi::internal; 18 | using namespace sdbusplus::server::xyz::openbmc_project::control; 19 | 20 | namespace sdbusRule = sdbusplus::bus::match::rules; 21 | 22 | namespace 23 | { 24 | using SoftPowerOffInherit = sdbusplus::server::object_t; 25 | } 26 | 27 | /** @class SoftPowerOff 28 | * @brief Responsible for coordinating Host SoftPowerOff operation 29 | */ 30 | class SoftPowerOff : public SoftPowerOffInherit 31 | { 32 | public: 33 | /** @brief Constructs SoftPowerOff object. 34 | * 35 | * @param[in] bus - system dbus handler 36 | * @param[in] event - sd_event handler 37 | * @param[in] objPath - The Dbus path hosting SoftPowerOff function 38 | */ 39 | SoftPowerOff(sdbusplus::bus_t& bus, sd_event* event, const char* objPath) : 40 | SoftPowerOffInherit(bus, objPath, 41 | SoftPowerOffInherit::action::defer_emit), 42 | bus(bus), timer(event), 43 | hostControlSignal( 44 | bus, 45 | sdbusRule::type::signal() + sdbusRule::member("CommandComplete") + 46 | sdbusRule::path("/xyz/openbmc_project/control/host0") + 47 | sdbusRule::interface(CONTROL_HOST_BUSNAME) + 48 | sdbusRule::argN(0, convertForMessage(Host::Command::SoftOff)), 49 | std::bind(std::mem_fn(&SoftPowerOff::hostControlEvent), this, 50 | std::placeholders::_1)) 51 | { 52 | // Need to announce since we may get the response 53 | // very quickly on host shutdown command 54 | emit_object_added(); 55 | 56 | // The whole purpose of this application is to send a host shutdown 57 | // command and watch for the soft power off to go through. We need 58 | // the interface added signal emitted before we send the shutdown 59 | // command just to attend to lightning fast response from host 60 | sendHostShutDownCmd(); 61 | } 62 | 63 | /** @brief Tells if the objective of this application is completed */ 64 | inline auto isCompleted() 65 | { 66 | return completed; 67 | } 68 | 69 | /** @brief Tells if the referenced timer is expired or not */ 70 | inline auto isTimerExpired() 71 | { 72 | return timer.isExpired(); 73 | } 74 | 75 | /** @brief overloaded property setter function 76 | * 77 | * @param[in] value - One of SoftOffReceived / HostShutdown 78 | * 79 | * @return Success or exception thrown 80 | */ 81 | HostResponse responseReceived(HostResponse value) override; 82 | 83 | /** @brief Using the base class's getter method */ 84 | using Base::SoftPowerOff::responseReceived; 85 | 86 | /** @brief Calls to start a timer 87 | * 88 | * @param[in] usec - Time in microseconds 89 | * 90 | * @return Success or exception thrown 91 | */ 92 | int startTimer(const std::chrono::microseconds& usec); 93 | 94 | private: 95 | // Need this to send SMS_ATTN 96 | // TODO : Switch over to using mapper service in a different patch 97 | static constexpr auto HOST_IPMI_BUS = "org.openbmc.HostIpmi"; 98 | static constexpr auto HOST_IPMI_OBJ = "/org/openbmc/HostIpmi/1"; 99 | static constexpr auto HOST_IPMI_INTF = "org.openbmc.HostIpmi"; 100 | 101 | /* @brief sdbusplus handle */ 102 | sdbusplus::bus_t& bus; 103 | 104 | /** @brief Reference to Timer object */ 105 | sdbusplus::Timer timer; 106 | 107 | /** @brief Marks the end of life of this application. 108 | * 109 | * This is set to true if host gives appropriate responses 110 | * for the sequence of commands. 111 | */ 112 | bool completed = false; 113 | 114 | /** @brief Subscribe to host control signals 115 | * 116 | * Protocol is to send the host power off request to the host 117 | * control interface and then wait for a signal indicating pass/fail 118 | **/ 119 | sdbusplus::bus::match_t hostControlSignal; 120 | 121 | /** @brief Sends host control command to tell host to shut down 122 | * 123 | * After sending the command, wait for a signal indicating the status 124 | * of the command. 125 | * 126 | * After receiving the initial response, start a timer for 30 minutes 127 | * to let host do a clean shutdown of partitions. When the response is 128 | * received from the host, it indicates that BMC can do a power off. 129 | * If BMC fails to get any response, then a hard power off would 130 | * be forced. 131 | * 132 | * @return - Does not return anything. Error will result in exception 133 | * being thrown 134 | */ 135 | void sendHostShutDownCmd(); 136 | 137 | /** @brief Callback function on host control signals 138 | * 139 | * @param[in] msg - Data associated with subscribed signal 140 | * 141 | */ 142 | void hostControlEvent(sdbusplus::message_t& msg); 143 | }; 144 | } // namespace ipmi 145 | } // namespace phosphor 146 | -------------------------------------------------------------------------------- /storageaddsel.cpp: -------------------------------------------------------------------------------- 1 | #include "error-HostEvent.hpp" 2 | #include "sensorhandler.hpp" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace std; 22 | using namespace phosphor::logging; 23 | using namespace sdbusplus::server::xyz::openbmc_project::logging; 24 | 25 | std::string readESEL(const char* fileName) 26 | { 27 | std::string content; 28 | std::ifstream handle(fileName); 29 | 30 | if (handle.fail()) 31 | { 32 | lg2::error("Failed to open eSEL, file name: {FILENAME}", "FILENAME", 33 | fileName); 34 | return content; 35 | } 36 | 37 | handle.seekg(0, std::ios::end); 38 | content.resize(handle.tellg()); 39 | handle.seekg(0, std::ios::beg); 40 | handle.read(&content[0], content.size()); 41 | handle.close(); 42 | 43 | return content; 44 | } 45 | 46 | void createProcedureLogEntry(uint8_t procedureNum) 47 | { 48 | // Read the eSEL data from the file. 49 | static constexpr auto eSELFile = "/tmp/esel"; 50 | auto eSELData = readESEL(eSELFile); 51 | 52 | // Each byte in eSEL is formatted as %02x with a space between bytes and 53 | // insert '/0' at the end of the character array. 54 | static constexpr auto byteSeparator = 3; 55 | std::unique_ptr data( 56 | new char[(eSELData.size() * byteSeparator) + 1]()); 57 | 58 | for (size_t i = 0; i < eSELData.size(); i++) 59 | { 60 | sprintf(&data[i * byteSeparator], "%02x ", eSELData[i]); 61 | } 62 | data[eSELData.size() * byteSeparator] = '\0'; 63 | 64 | using error = sdbusplus::error::org::open_power::host::MaintenanceProcedure; 65 | using metadata = org::open_power::host::MaintenanceProcedure; 66 | 67 | report(metadata::ESEL(data.get()), 68 | metadata::PROCEDURE(static_cast(procedureNum))); 69 | } 70 | -------------------------------------------------------------------------------- /storageaddsel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | /** @brief Read eSEL data into a string 8 | * 9 | * @param[in] filename - filename of file containing eSEL 10 | * 11 | * @return On success return the eSEL data 12 | */ 13 | std::string readESEL(const char* filename); 14 | 15 | /** @brief Create a log entry with maintenance procedure 16 | * 17 | * @param[in] procedureNum - procedure number associated with the log entry 18 | */ 19 | void createProcedureLogEntry(uint8_t procedureNum); 20 | -------------------------------------------------------------------------------- /subprojects/boost.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = boost-1.84.0 3 | 4 | source_url = https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.gz 5 | source_hash = 4d27e9efed0f6f152dc28db6430b9d3dfb40c0345da7342eaa5a987dde57bd95 6 | source_filename = 1_84_0.tar.gz 7 | 8 | [provide] 9 | boost = boost_dep 10 | -------------------------------------------------------------------------------- /subprojects/nlohmann_json.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | revision = HEAD 3 | url = https://github.com/nlohmann/json.git 4 | 5 | [provide] 6 | nlohmann_json = nlohmann_json_dep 7 | -------------------------------------------------------------------------------- /subprojects/phosphor-dbus-interfaces.wrap: -------------------------------------------------------------------------------- 1 | 2 | [wrap-git] 3 | url = https://github.com/openbmc/phosphor-dbus-interfaces.git 4 | revision = HEAD 5 | 6 | [provide] 7 | phosphor-dbus-interfaces = phosphor_dbus_interfaces_dep 8 | -------------------------------------------------------------------------------- /subprojects/phosphor-logging.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/openbmc/phosphor-logging 3 | revision = HEAD 4 | 5 | [provide] 6 | phosphor-logging = phosphor_logging_dep 7 | -------------------------------------------------------------------------------- /subprojects/sdbusplus.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/openbmc/sdbusplus 3 | revision = HEAD 4 | 5 | [provide] 6 | sdbusplus = sdbusplus_dep 7 | -------------------------------------------------------------------------------- /subprojects/sdeventplus.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/openbmc/sdeventplus 3 | revision = HEAD 4 | 5 | [provide] 6 | sdeventplus = sdeventplus_dep 7 | -------------------------------------------------------------------------------- /sys_info_param.cpp: -------------------------------------------------------------------------------- 1 | #include "sys_info_param.hpp" 2 | 3 | std::tuple SysInfoParamStore::lookup( 4 | uint8_t paramSelector) const 5 | { 6 | const auto iterator = params.find(paramSelector); 7 | if (iterator == params.end()) 8 | { 9 | return std::make_tuple(false, ""); 10 | } 11 | 12 | auto& callback = iterator->second; 13 | auto s = callback(); 14 | return std::make_tuple(true, s); 15 | } 16 | 17 | void SysInfoParamStore::update(uint8_t paramSelector, const std::string& s) 18 | { 19 | // Add a callback that captures a copy of the string passed and returns it 20 | // when invoked. 21 | 22 | // clang-format off 23 | update(paramSelector, [s]() { 24 | return s; 25 | }); 26 | // clang-format on 27 | } 28 | 29 | void SysInfoParamStore::update(uint8_t paramSelector, 30 | const std::function& callback) 31 | { 32 | params[paramSelector] = callback; 33 | } 34 | -------------------------------------------------------------------------------- /sys_info_param.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /** 10 | * Key-value store for string-type system info parameters. 11 | */ 12 | class SysInfoParamStoreIntf 13 | { 14 | public: 15 | virtual ~SysInfoParamStoreIntf() {} 16 | 17 | /** 18 | * Returns true if parameter is found. If and only if s is non-null, 19 | * invokes the parameter's callback and writes the value. 20 | * 21 | * @param[in] paramSelector - the key to lookup. 22 | * @return tuple of bool and string, true if parameter is found and 23 | * string set accordingly. 24 | */ 25 | virtual std::tuple lookup( 26 | uint8_t paramSelector) const = 0; 27 | 28 | /** 29 | * Update a parameter by its code with a string value. 30 | * 31 | * @param[in] paramSelector - the key to update. 32 | * @param[in] s - the value to set. 33 | */ 34 | virtual void update(uint8_t paramSelector, const std::string& s) = 0; 35 | 36 | /** 37 | * Update a parameter by its code with a callback that is called to retrieve 38 | * its value whenever called. Callback must be idempotent, as it may be 39 | * called multiple times by the host to retrieve the parameter by chunks. 40 | * 41 | * @param[in] paramSelector - the key to update. 42 | * @param[in] callback - the callback to use for parameter retrieval. 43 | */ 44 | virtual void update(uint8_t paramSelector, 45 | const std::function& callback) = 0; 46 | 47 | // TODO: Store "read-only" flag for each parameter. 48 | // TODO: Function to erase a parameter? 49 | }; 50 | 51 | /** 52 | * Implement the system info parameters store as a map of callbacks. 53 | */ 54 | class SysInfoParamStore : public SysInfoParamStoreIntf 55 | { 56 | public: 57 | std::tuple lookup(uint8_t paramSelector) const override; 58 | void update(uint8_t paramSelector, const std::string& s) override; 59 | void update(uint8_t paramSelector, 60 | const std::function& callback) override; 61 | 62 | private: 63 | std::map> params; 64 | }; 65 | -------------------------------------------------------------------------------- /systemintfcmds.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // These are per skiboot ipmi-sel code 6 | 7 | // Minor command for soft shurdown 8 | #define SOFT_OFF 0x00 9 | // Major command for Any kind of power ops 10 | #define CMD_POWER 0x04 11 | // Major command for the heartbeat operation (verify host is alive) 12 | #define CMD_HEARTBEAT 0xFF 13 | -------------------------------------------------------------------------------- /test/meson.build: -------------------------------------------------------------------------------- 1 | gtest = dependency('gtest', main: true, disabler: true, required: false) 2 | gmock = dependency('gmock', disabler: true, required: false) 3 | if not gtest.found() or not gmock.found() 4 | gtest_opts = import('cmake').subproject_options() 5 | gtest_opts.add_cmake_defines({'CMAKE_CXX_FLAGS': '-Wno-pedantic'}) 6 | gtest_proj = import('cmake').subproject( 7 | 'googletest', 8 | options: gtest_opts, 9 | required: false, 10 | ) 11 | if gtest_proj.found() 12 | gtest = declare_dependency( 13 | dependencies: [ 14 | dependency('threads'), 15 | gtest_proj.dependency('gtest'), 16 | gtest_proj.dependency('gtest_main'), 17 | ], 18 | ) 19 | gmock = gtest_proj.dependency('gmock') 20 | else 21 | assert(not get_option('tests').enabled(), 'Googletest is required') 22 | endif 23 | endif 24 | 25 | test( 26 | 'entitymap_json', 27 | executable( 28 | 'entitymap_json', 29 | 'entitymap_json_unittest.cpp', 30 | include_directories: root_inc, 31 | build_by_default: false, 32 | implicit_include_directories: false, 33 | dependencies: [ 34 | entity_map_json_dep, 35 | gmock, 36 | gtest, 37 | nlohmann_json_dep, 38 | sdbusplus_dep, 39 | ], 40 | ), 41 | ) 42 | 43 | # Build/add oemrouter_unittest to test suite 44 | # Issue #3325 45 | # test('oemrouter', 46 | # executable( 47 | # 'oemrouter', 48 | # 'oemrouter_unittest.cpp', 49 | # include_directories: root_inc, 50 | # build_by_default: false, 51 | # implicit_include_directories: false, 52 | # dependencies: [gtest, gmock] 53 | # )) 54 | 55 | # Build/add message packing/unpacking unit tests 56 | test( 57 | 'message', 58 | executable( 59 | 'message', 60 | 'message/pack.cpp', 61 | 'message/payload.cpp', 62 | 'message/unpack.cpp', 63 | include_directories: root_inc, 64 | build_by_default: false, 65 | override_options: ['b_lundef=true'], 66 | implicit_include_directories: false, 67 | dependencies: [ 68 | boost, 69 | crypto, 70 | gmock, 71 | gtest, 72 | libsystemd_dep, 73 | phosphor_logging_dep, 74 | sdbusplus_dep, 75 | ], 76 | ), 77 | ) 78 | 79 | # Build/add closesession_unittest to test suite 80 | test( 81 | 'session/closesession', 82 | executable( 83 | 'session_closesession', 84 | 'session/closesession_unittest.cpp', 85 | include_directories: root_inc, 86 | build_by_default: false, 87 | implicit_include_directories: false, 88 | dependencies: [gtest, gmock], 89 | ), 90 | ) 91 | 92 | # Build/add sensorcommands_unittest to test suite 93 | test( 94 | 'dbus-sdr/sensorcommands', 95 | executable( 96 | 'dbus-sdr_sensorcommands', 97 | 'dbus-sdr/sensorcommands_unittest.cpp', 98 | include_directories: root_inc, 99 | build_by_default: false, 100 | implicit_include_directories: false, 101 | dependencies: [sensorutils_dep, gtest, gmock], 102 | ), 103 | ) 104 | -------------------------------------------------------------------------------- /test/oemrouter_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | // Watch for correct singleton behavior. 11 | static oem::Router* singletonUnderTest; 12 | 13 | static ipmid_callback_t wildHandler; 14 | 15 | static ipmi_netfn_t lastNetFunction; 16 | 17 | // Fake ipmi_register_callback() for this test. 18 | void ipmi_register_callback(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 19 | ipmi_context_t context, ipmid_callback_t cb, 20 | ipmi_cmd_privilege_t priv) 21 | { 22 | EXPECT_EQ(ipmi::netFnOem, netfn); 23 | EXPECT_EQ(ipmi::cmdWildcard, cmd); 24 | EXPECT_EQ(reinterpret_cast(singletonUnderTest), context); 25 | EXPECT_EQ(PRIVILEGE_OEM, priv); 26 | lastNetFunction = netfn; 27 | wildHandler = cb; 28 | } 29 | 30 | namespace oem 31 | { 32 | 33 | namespace 34 | { 35 | void MakeRouter() 36 | { 37 | if (!singletonUnderTest) 38 | { 39 | singletonUnderTest = mutableRouter(); 40 | } 41 | ASSERT_EQ(singletonUnderTest, mutableRouter()); 42 | } 43 | 44 | void ActivateRouter() 45 | { 46 | MakeRouter(); 47 | singletonUnderTest->activate(); 48 | ASSERT_EQ(ipmi::netFnOem, lastNetFunction); 49 | } 50 | 51 | void RegisterWithRouter(Number oen, ipmi_cmd_t cmd, Handler cb) 52 | { 53 | ActivateRouter(); 54 | singletonUnderTest->registerHandler(oen, cmd, cb); 55 | } 56 | 57 | uint8_t msgPlain[] = {0x56, 0x34, 0x12}; 58 | uint8_t replyPlain[] = {0x56, 0x34, 0x12, 0x31, 0x41}; 59 | uint8_t msgPlus2[] = {0x67, 0x45, 0x23, 0x10, 0x20}; 60 | uint8_t msgBadOen[] = {0x57, 0x34, 0x12}; 61 | 62 | void RegisterTwoWays(ipmi_cmd_t* nextCmd) 63 | { 64 | Handler f = [](ipmi_cmd_t cmd, [[maybe_unused]] const uint8_t* reqBuf, 65 | uint8_t* replyBuf, size_t* dataLen) { 66 | // Check inputs 67 | EXPECT_EQ(0x78, cmd); 68 | EXPECT_EQ(0, *dataLen); // Excludes OEN 69 | 70 | // Generate reply. 71 | *dataLen = 2; 72 | std::memcpy(replyBuf, replyPlain + 3, *dataLen); 73 | return 0; 74 | }; 75 | RegisterWithRouter(0x123456, 0x78, f); 76 | 77 | *nextCmd = ipmi::cmdWildcard; 78 | Handler g = [nextCmd](ipmi_cmd_t cmd, const uint8_t* reqBuf, 79 | [[maybe_unused]] uint8_t* replyBuf, size_t* dataLen) { 80 | // Check inputs 81 | EXPECT_EQ(*nextCmd, cmd); 82 | EXPECT_EQ(2, *dataLen); // Excludes OEN 83 | if (2 != *dataLen) 84 | { 85 | return 0xE0; 86 | } 87 | EXPECT_EQ(msgPlus2[3], reqBuf[0]); 88 | EXPECT_EQ(msgPlus2[4], reqBuf[1]); 89 | 90 | // Generate reply. 91 | *dataLen = 0; 92 | return 0; 93 | }; 94 | RegisterWithRouter(0x234567, ipmi::cmdWildcard, g); 95 | } 96 | } // namespace 97 | 98 | TEST(OemRouterTest, MakeRouterProducesConsistentSingleton) 99 | { 100 | MakeRouter(); 101 | } 102 | 103 | TEST(OemRouterTest, ActivateRouterSetsLastNetToOEMGROUP) 104 | { 105 | lastNetFunction = 0; 106 | ActivateRouter(); 107 | } 108 | 109 | TEST(OemRouterTest, VerifiesSpecificCommandMatches) 110 | { 111 | ipmi_cmd_t cmd; 112 | uint8_t reply[256]; 113 | size_t dataLen; 114 | 115 | RegisterTwoWays(&cmd); 116 | 117 | dataLen = 3; 118 | EXPECT_EQ(0, wildHandler(ipmi::netFnOem, 0x78, msgPlain, reply, &dataLen, 119 | nullptr)); 120 | EXPECT_EQ(5, dataLen); 121 | EXPECT_EQ(replyPlain[0], reply[0]); 122 | EXPECT_EQ(replyPlain[1], reply[1]); 123 | EXPECT_EQ(replyPlain[2], reply[2]); 124 | EXPECT_EQ(replyPlain[3], reply[3]); 125 | EXPECT_EQ(replyPlain[4], reply[4]); 126 | } 127 | 128 | TEST(OemRouterTest, WildCardMatchesTwoRandomCodes) 129 | { 130 | ipmi_cmd_t cmd; 131 | uint8_t reply[256]; 132 | size_t dataLen; 133 | 134 | RegisterTwoWays(&cmd); 135 | 136 | // Check two random command codes. 137 | dataLen = 5; 138 | cmd = 0x89; 139 | EXPECT_EQ(0, wildHandler(ipmi::netFnOem, cmd, msgPlus2, reply, &dataLen, 140 | nullptr)); 141 | EXPECT_EQ(3, dataLen); 142 | 143 | dataLen = 5; 144 | cmd = 0x67; 145 | EXPECT_EQ(0, wildHandler(ipmi::netFnOem, cmd, msgPlus2, reply, &dataLen, 146 | nullptr)); 147 | EXPECT_EQ(3, dataLen); 148 | } 149 | 150 | TEST(OemRouterTest, CommandsAreRejectedIfInvalid) 151 | { 152 | ipmi_cmd_t cmd; 153 | uint8_t reply[256]; 154 | size_t dataLen; 155 | 156 | RegisterTwoWays(&cmd); 157 | 158 | // Message too short to include whole OEN? 159 | dataLen = 2; 160 | EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID, 161 | wildHandler(ipmi::netFnOem, 0x78, msgPlain, reply, &dataLen, 162 | nullptr)); 163 | 164 | // Wrong specific command? 165 | dataLen = 3; 166 | EXPECT_EQ(IPMI_CC_INVALID, wildHandler(ipmi::netFnOem, 0x89, msgPlain, 167 | reply, &dataLen, nullptr)); 168 | 169 | // Wrong OEN? 170 | dataLen = 3; 171 | EXPECT_EQ(IPMI_CC_INVALID, wildHandler(ipmi::netFnOem, 0x78, msgBadOen, 172 | reply, &dataLen, nullptr)); 173 | } 174 | 175 | } // namespace oem 176 | -------------------------------------------------------------------------------- /test/session/closesession_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | TEST(parseSessionInputPayloadTest, ValidObjectPath) 6 | { 7 | uint32_t sessionId = 0; 8 | uint8_t sessionHandle = 0; 9 | std::string objectPath = 10 | "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; 11 | 12 | EXPECT_TRUE( 13 | parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle)); 14 | EXPECT_EQ(0x12a4567d, sessionId); 15 | EXPECT_EQ(0x8a, sessionHandle); 16 | } 17 | 18 | TEST(parseSessionInputPayloadTest, InvalidObjectPath) 19 | { 20 | uint32_t sessionId = 0; 21 | uint8_t sessionHandle = 0; 22 | // A valid object path will be like 23 | // "/xyz/openbmc_project/ipmi/session/channel/sessionId_sessionHandle" 24 | // Ex: "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a" 25 | // SessionId : 0X12a4567d 26 | // SessionHandle: 0X8a 27 | std::string objectPath = "/xyz/openbmc_project/ipmi/session/eth0/12a4567d"; 28 | 29 | EXPECT_FALSE( 30 | parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle)); 31 | } 32 | 33 | TEST(parseSessionInputPayloadTest, NoObjectPath) 34 | { 35 | uint32_t sessionId = 0; 36 | uint8_t sessionHandle = 0; 37 | std::string objectPath; 38 | 39 | EXPECT_FALSE( 40 | parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle)); 41 | } 42 | 43 | TEST(isSessionObjectMatchedTest, ValidSessionId) 44 | { 45 | std::string objectPath = 46 | "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; 47 | uint32_t sessionId = 0x12a4567d; 48 | uint8_t sessionHandle = 0; 49 | 50 | EXPECT_TRUE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); 51 | } 52 | 53 | TEST(isSessionObjectMatchedTest, ValidSessionHandle) 54 | { 55 | std::string objectPath = 56 | "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; 57 | uint32_t sessionId = 0; 58 | uint8_t sessionHandle = 0x8a; 59 | 60 | EXPECT_TRUE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); 61 | } 62 | 63 | TEST(isSessionObjectMatchedTest, InvalidSessionId) 64 | { 65 | std::string objectPath = 66 | "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; 67 | uint32_t sessionId = 0x1234b67d; 68 | uint8_t sessionHandle = 0; 69 | 70 | EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); 71 | } 72 | 73 | TEST(isSessionObjectMatchedTest, InvalidSessionHandle) 74 | { 75 | std::string objectPath = 76 | "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; 77 | uint32_t sessionId = 0; 78 | uint8_t sessionHandle = 0x9b; 79 | 80 | EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); 81 | } 82 | 83 | TEST(isSessionObjectMatchedTest, ZeroSessionId_ZeroSessionHandle) 84 | { 85 | std::string objectPath = 86 | "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; 87 | uint32_t sessionId = 0; 88 | uint8_t sessionHandle = 0; 89 | 90 | EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); 91 | } 92 | 93 | TEST(isSessionObjectMatchedTest, InvalidObjectPath) 94 | { 95 | // A valid object path will be like 96 | // "/xyz/openbmc_project/ipmi/session/channel/sessionId_sessionHandle" 97 | // Ex: "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a" 98 | // SessionId : 0X12a4567d 99 | // SessionHandle: 0X8a 100 | std::string objectPath = "/xyz/openbmc_project/ipmi/session/eth0/12a4567d"; 101 | uint32_t sessionId = 0x12a4567d; 102 | uint8_t sessionHandle = 0; 103 | 104 | EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); 105 | } 106 | 107 | TEST(isSessionObjectMatchedTest, NoObjectPath) 108 | { 109 | std::string objectPath; 110 | uint32_t sessionId = 0x12a4567d; 111 | uint8_t sessionHandle = 0x8a; 112 | 113 | EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); 114 | } 115 | -------------------------------------------------------------------------------- /transport/meson.build: -------------------------------------------------------------------------------- 1 | if get_option('transport-implementation') == 'serial' 2 | subdir('serialbridge') 3 | endif 4 | -------------------------------------------------------------------------------- /transport/serialbridge/meson.build: -------------------------------------------------------------------------------- 1 | CLI11_dep = dependency('CLI11') 2 | 3 | deps = [ 4 | dependency('libsystemd'), 5 | dependency('systemd'), 6 | sdeventplus_dep, 7 | stdplus_dep, 8 | sdbusplus_dep, 9 | phosphor_logging_dep, 10 | CLI11_dep, 11 | ] 12 | 13 | serialbridged = executable( 14 | 'serialbridged', 15 | 'serialbridged.cpp', 16 | 'serialcmd.cpp', 17 | dependencies: deps, 18 | install: true, 19 | install_dir: get_option('libexecdir'), 20 | ) 21 | 22 | # Configure and install systemd unit files 23 | systemd = dependency('systemd') 24 | if systemd.found() 25 | conf_data = configuration_data() 26 | conf_data.set( 27 | 'BIN', 28 | get_option('prefix') / get_option('libexecdir') / serialbridged.name(), 29 | ) 30 | configure_file( 31 | input: 'serialbridge@.service.in', 32 | output: 'serialbridge@.service', 33 | configuration: conf_data, 34 | install: true, 35 | install_dir: systemd.get_variable(pkgconfig: 'systemdsystemunitdir'), 36 | ) 37 | endif 38 | 39 | if not get_option('tests').disabled() 40 | subdir('test') 41 | endif 42 | -------------------------------------------------------------------------------- /transport/serialbridge/serialbridge@.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Phosphor IPMI Serial DBus Bridge 3 | StartLimitBurst=3 4 | StartLimitIntervalSec=300 5 | After=phosphor-ipmi-host.service 6 | 7 | [Service] 8 | Restart=always 9 | RestartSec=10 10 | TimeoutStartSec=60 11 | TimeoutStopSec=60 12 | ExecStartPre=/bin/stty -F /dev/"%i" 115200 litout -crtscts -ixon -echo raw 13 | ExecStart=@BIN@ -d "%i" 14 | SyslogIdentifier=serialbridged-%i 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | RequiredBy= 19 | -------------------------------------------------------------------------------- /transport/serialbridge/serialbridged.cpp: -------------------------------------------------------------------------------- 1 | #include "serialcmd.hpp" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace serialbridge 18 | { 19 | 20 | using sdeventplus::source::IO; 21 | using sdeventplus::source::Signal; 22 | using stdplus::fd::OpenAccess; 23 | using stdplus::fd::OpenFlag; 24 | using stdplus::fd::OpenFlags; 25 | 26 | int execute(const std::string& channel, const bool& verbose) 27 | { 28 | // Set up DBus and event loop 29 | auto event = sdeventplus::Event::get_default(); 30 | auto bus = sdbusplus::bus::new_default(); 31 | bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL); 32 | 33 | // Configure basic signal handling 34 | auto exit_handler = [&event](Signal&, const struct signalfd_siginfo*) { 35 | lg2::error("Interrupted, Exiting\n"); 36 | event.exit(0); 37 | }; 38 | stdplus::signal::block(SIGINT); 39 | Signal sig_init(event, SIGINT, exit_handler); 40 | stdplus::signal::block(SIGTERM); 41 | Signal sig_term(event, SIGTERM, exit_handler); 42 | 43 | // Open an FD for the UART channel 44 | stdplus::ManagedFd uart = stdplus::fd::open( 45 | std::format("/dev/{}", channel.c_str()), 46 | OpenFlags(OpenAccess::ReadWrite).set(OpenFlag::NonBlock)); 47 | sdbusplus::slot_t slot(nullptr); 48 | 49 | std::unique_ptr serialchannel = 50 | std::make_unique(verbose); 51 | 52 | // Add a reader to the bus for handling inbound IPMI 53 | IO ioSource(event, uart.get(), EPOLLIN | EPOLLET, 54 | stdplus::exception::ignore( 55 | [&serialchannel, &uart, &bus, &slot](IO&, int, uint32_t) { 56 | serialchannel->read(uart, bus, slot); 57 | })); 58 | 59 | sd_notify(0, "READY=1"); 60 | return event.loop(); 61 | } 62 | 63 | } // namespace serialbridge 64 | 65 | int main(int argc, char* argv[]) 66 | { 67 | std::string device; 68 | bool verbose = 0; 69 | 70 | // Parse input parameter 71 | CLI::App app("Serial IPMI Bridge"); 72 | app.add_option("-d,--device", device, "select uart device"); 73 | app.add_option("-v,--verbose", verbose, "enable debug message"); 74 | CLI11_PARSE(app, argc, argv); 75 | 76 | try 77 | { 78 | return serialbridge::execute(device, verbose); 79 | } 80 | catch (const std::exception& e) 81 | { 82 | lg2::error("FAILED: {MSG}\n", "MSG", e); 83 | return 1; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /transport/serialbridge/serialcmd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace serialbridge 8 | { 9 | 10 | static constexpr auto bmStart = 0xA0; 11 | static constexpr auto bmStop = 0xA5; 12 | static constexpr auto bmHandshake = 0xA6; 13 | static constexpr auto bmEscape = 0xAA; 14 | 15 | static constexpr auto ipmiSerialConnectionHeaderLength = 3; 16 | static constexpr auto ipmiSerialChecksumSize = 1; 17 | static constexpr auto ipmiSerialMaxBufferSize = 256; 18 | 19 | /** 20 | * @brief IPMI Serial Message Structure 21 | */ 22 | struct IpmiSerialHeader 23 | { 24 | uint8_t rsAddr; 25 | uint8_t rsNetFnLUN; 26 | uint8_t checksum1; 27 | uint8_t rqAddr; 28 | uint8_t rqSeqLUN; 29 | uint8_t cmd; 30 | } __attribute__((packed)); 31 | 32 | class SerialChannel 33 | { 34 | public: 35 | static constexpr uint8_t netFnShift = 2; 36 | static constexpr uint8_t lunMask = (1 << netFnShift) - 1; 37 | 38 | SerialChannel(bool debug) : verbose(debug), msgState(MsgState::msgIdle) {}; 39 | 40 | int write(stdplus::Fd& uart, uint8_t rsAddr, uint8_t rqAddr, uint8_t seq, 41 | sdbusplus::message_t&& m); 42 | void read(stdplus::Fd& serial, sdbusplus::bus_t& bus, 43 | sdbusplus::slot_t& outstanding); 44 | uint8_t calculateChecksum(std::span data); 45 | uint8_t getUnescapedCharacter(uint8_t c); 46 | int consumeIpmiSerialPacket(std::span& escapedDataBytes, 47 | std::vector& unescapedDataBytes); 48 | uint8_t processEscapedCharacter(std::vector& buffer, 49 | const std::vector& data); 50 | 51 | private: 52 | bool verbose; 53 | enum class MsgState 54 | { 55 | msgIdle = 0, 56 | msgInProgress, 57 | msgInEscape, 58 | }; 59 | MsgState msgState; 60 | std::vector requestBuffer; 61 | std::vector responseBuffer; 62 | }; 63 | 64 | } // namespace serialbridge 65 | -------------------------------------------------------------------------------- /transport/serialbridge/test/meson.build: -------------------------------------------------------------------------------- 1 | gtest = dependency('gtest', main: true, disabler: true, required: false) 2 | gmock = dependency('gmock', disabler: true, required: false) 3 | if not gtest.found() or not gmock.found() 4 | gtest_opts = import('cmake').subproject_options() 5 | gtest_opts.add_cmake_defines({'CMAKE_CXX_FLAGS': '-Wno-pedantic'}) 6 | gtest_proj = import('cmake').subproject( 7 | 'googletest', 8 | options: gtest_opts, 9 | required: false, 10 | ) 11 | if gtest_proj.found() 12 | gtest = declare_dependency( 13 | dependencies: [ 14 | dependency('threads'), 15 | gtest_proj.dependency('gtest'), 16 | gtest_proj.dependency('gtest_main'), 17 | ], 18 | ) 19 | gmock = gtest_proj.dependency('gmock') 20 | else 21 | assert(not get_option('tests').enabled(), 'Googletest is required') 22 | endif 23 | endif 24 | 25 | # Build/add serial_unittest to test suite 26 | test( 27 | 'transport_serial', 28 | executable( 29 | 'transport_serial_unittest', 30 | 'serial_unittest.cpp', 31 | '../serialcmd.cpp', 32 | include_directories: root_inc, 33 | build_by_default: false, 34 | implicit_include_directories: false, 35 | dependencies: [ 36 | sdbusplus_dep, 37 | stdplus_dep, 38 | phosphor_logging_dep, 39 | sdeventplus_dep, 40 | gtest, 41 | gmock, 42 | ], 43 | ), 44 | ) 45 | -------------------------------------------------------------------------------- /transport/serialbridge/test/serial_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace serialbridge 6 | { 7 | 8 | /** 9 | * @brief Table of special characters 10 | */ 11 | std::unordered_map testsets = { 12 | {bmStart, 0xB0}, /* start */ 13 | {bmStop, 0xB5}, /* stop */ 14 | {bmHandshake, 0xB6}, /* packet handshake */ 15 | {bmEscape, 0xBA}, /* data escape */ 16 | {0x1B, 0x3B} /* escape */ 17 | }; 18 | 19 | TEST(TestSpecialCharact, getUnescapedCharact) 20 | { 21 | uint8_t c; 22 | auto channel = std::make_shared(0); 23 | 24 | for (const auto& set : testsets) 25 | { 26 | c = channel->getUnescapedCharacter(set.second); 27 | ASSERT_EQ(c, set.first); 28 | } 29 | } 30 | 31 | TEST(TestSpecialCharact, processEscapedCharacter) 32 | { 33 | std::vector buffer; 34 | uint8_t unescaped = 0xd0; 35 | auto channel = std::make_shared(0); 36 | 37 | channel->processEscapedCharacter(buffer, std::vector{bmStart}); 38 | 39 | ASSERT_EQ(buffer.at(0), bmEscape); 40 | ASSERT_EQ(buffer.at(1), testsets.at(bmStart)); 41 | 42 | buffer.clear(); 43 | channel->processEscapedCharacter(buffer, std::vector{unescaped}); 44 | 45 | ASSERT_EQ(buffer.at(0), unescaped); 46 | } 47 | 48 | TEST(TestChecksum, calculateChecksum) 49 | { 50 | std::array dataBytes{0x01, 0x10, 0x60, 0xf0, 0x50}; 51 | auto channel = std::make_shared(0); 52 | 53 | uint8_t checksum = 54 | channel->calculateChecksum(std::span(dataBytes)); 55 | 56 | checksum += (~checksum) + 1; 57 | ASSERT_EQ(checksum, 0); 58 | } 59 | 60 | TEST(TestIpmiSerialPacket, consumeIpmiSerialPacket) 61 | { 62 | std::vector dataBytes{bmStart, 0x20, 0x18, 0xc8, 0x81, 63 | 0xc, 0x46, 0x01, 0x2c, bmStop}; 64 | std::vector dataBytesSplit1{bmStart, 0x20, 0x18, 0xc8}; 65 | std::vector dataBytesSplit2{0x81, 0xc, 0x46, 0x01, 0x2c, bmStop}; 66 | std::span input(dataBytes); 67 | std::span input1(dataBytesSplit1); 68 | std::span input2(dataBytesSplit2); 69 | std::vector output; 70 | 71 | auto channel = std::make_shared(0); 72 | 73 | auto result = channel->consumeIpmiSerialPacket(input, output); 74 | 75 | ASSERT_EQ(result, true); 76 | 77 | output.clear(); 78 | result = channel->consumeIpmiSerialPacket(input1, output); 79 | ASSERT_EQ(result, false); 80 | result = channel->consumeIpmiSerialPacket(input2, output); 81 | ASSERT_EQ(result, true); 82 | } 83 | 84 | } // namespace serialbridge 85 | -------------------------------------------------------------------------------- /transportconstants.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace ipmi 9 | { 10 | namespace transport 11 | { 12 | 13 | using stdplus::operator""_zsv; 14 | 15 | // D-Bus Network Daemon definitions 16 | constexpr auto PATH_ROOT = "/xyz/openbmc_project/network"_zsv; 17 | constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface"; 18 | constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP"; 19 | constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create"; 20 | constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress"; 21 | constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor"; 22 | constexpr auto INTF_NEIGHBOR_CREATE_STATIC = 23 | "xyz.openbmc_project.Network.Neighbor.CreateStatic"; 24 | constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN"; 25 | constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create"; 26 | 27 | /** @brief IPMI LAN Parameters */ 28 | enum class LanParam : uint8_t 29 | { 30 | SetStatus = 0, 31 | AuthSupport = 1, 32 | AuthEnables = 2, 33 | IP = 3, 34 | IPSrc = 4, 35 | MAC = 5, 36 | SubnetMask = 6, 37 | Gateway1 = 12, 38 | Gateway1MAC = 13, 39 | VLANId = 20, 40 | CiphersuiteSupport = 22, 41 | CiphersuiteEntries = 23, 42 | cipherSuitePrivilegeLevels = 24, 43 | IPFamilySupport = 50, 44 | IPFamilyEnables = 51, 45 | IPv6Status = 55, 46 | IPv6StaticAddresses = 56, 47 | IPv6DynamicAddresses = 59, 48 | IPv6RouterControl = 64, 49 | IPv6StaticRouter1IP = 65, 50 | IPv6StaticRouter1MAC = 66, 51 | IPv6StaticRouter1PrefixLength = 67, 52 | IPv6StaticRouter1PrefixValue = 68, 53 | }; 54 | 55 | /** @brief IPMI IP Origin Types */ 56 | enum class IPSrc : uint8_t 57 | { 58 | Unspecified = 0, 59 | Static = 1, 60 | DHCP = 2, 61 | BIOS = 3, 62 | BMC = 4, 63 | }; 64 | 65 | /** @brief IPMI Set Status */ 66 | enum class SetStatus : uint8_t 67 | { 68 | Complete = 0, 69 | InProgress = 1, 70 | Commit = 2, 71 | }; 72 | 73 | /** @brief IPMI Family Suport Bits */ 74 | namespace IPFamilySupportFlag 75 | { 76 | constexpr uint8_t IPv6Only = 0; 77 | constexpr uint8_t DualStack = 1; 78 | constexpr uint8_t IPv6Alerts = 2; 79 | } // namespace IPFamilySupportFlag 80 | 81 | /** @brief IPMI IPFamily Enables Flag */ 82 | enum class IPFamilyEnables : uint8_t 83 | { 84 | IPv4Only = 0, 85 | IPv6Only = 1, 86 | DualStack = 2, 87 | }; 88 | 89 | /** @brief IPMI IPv6 Dyanmic Status Bits */ 90 | namespace IPv6StatusFlag 91 | { 92 | constexpr uint8_t DHCP = 0; 93 | constexpr uint8_t SLAAC = 1; 94 | }; // namespace IPv6StatusFlag 95 | 96 | /** @brief IPMI IPv6 Source */ 97 | enum class IPv6Source : uint8_t 98 | { 99 | Static = 0, 100 | SLAAC = 1, 101 | DHCP = 2, 102 | }; 103 | 104 | /** @brief IPMI IPv6 Address Status */ 105 | enum class IPv6AddressStatus : uint8_t 106 | { 107 | Active = 0, 108 | Disabled = 1, 109 | }; 110 | 111 | namespace IPv6RouterControlFlag 112 | { 113 | constexpr uint8_t Static = 0; 114 | constexpr uint8_t Dynamic = 1; 115 | }; // namespace IPv6RouterControlFlag 116 | 117 | // LAN Handler specific response codes 118 | constexpr Cc ccParamNotSupported = 0x80; 119 | constexpr Cc ccParamSetLocked = 0x81; 120 | constexpr Cc ccParamReadOnly = 0x82; 121 | 122 | // VLANs are a 12-bit value 123 | constexpr uint16_t VLAN_VALUE_MASK = 0x0fff; 124 | constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000; 125 | 126 | // Arbitrary v4 Address Limits 127 | constexpr uint8_t MAX_IPV4_ADDRESSES = 2; 128 | 129 | // Arbitrary v6 Address Limits to prevent too much output in ipmitool 130 | constexpr uint8_t MAX_IPV6_STATIC_ADDRESSES = 15; 131 | constexpr uint8_t MAX_IPV6_DYNAMIC_ADDRESSES = 15; 132 | 133 | // Prefix length limits of phosphor-networkd 134 | constexpr uint8_t MIN_IPV4_PREFIX_LENGTH = 1; 135 | constexpr uint8_t MAX_IPV4_PREFIX_LENGTH = 32; 136 | constexpr uint8_t MIN_IPV6_PREFIX_LENGTH = 1; 137 | constexpr uint8_t MAX_IPV6_PREFIX_LENGTH = 128; 138 | 139 | /** @enum SolConfParam 140 | * 141 | * using for Set/Get SOL configuration parameters command. 142 | */ 143 | enum class SolConfParam : uint8_t 144 | { 145 | Progress, //!< Set In Progress. 146 | Enable, //!< SOL Enable. 147 | Authentication, //!< SOL Authentication. 148 | Accumulate, //!< Character Accumulate Interval & Send Threshold. 149 | Retry, //!< SOL Retry. 150 | NonVbitrate, //!< SOL non-volatile bit rate. 151 | Vbitrate, //!< SOL volatile bit rate. 152 | Channel, //!< SOL payload channel. 153 | Port, //!< SOL payload port. 154 | }; 155 | 156 | constexpr uint8_t ipmiCCParamNotSupported = 0x80; 157 | constexpr uint8_t ipmiCCWriteReadParameter = 0x82; 158 | 159 | } // namespace transport 160 | } // namespace ipmi 161 | -------------------------------------------------------------------------------- /user_channel/channel_layer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | // Copyright (c) 2018 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | */ 16 | 17 | #include "channel_layer.hpp" 18 | 19 | #include "channel_mgmt.hpp" 20 | #include "cipher_mgmt.hpp" 21 | 22 | #include 23 | 24 | namespace ipmi 25 | { 26 | 27 | bool doesDeviceExist(const uint8_t chNum) 28 | { 29 | // TODO: This is not the reliable way to find the device 30 | // associated with ethernet interface as the channel number to 31 | // eth association is not done. Need to revisit later 32 | struct stat fileStat = {}; 33 | std::string devName("/sys/class/net/" + getChannelName(chNum)); 34 | 35 | if (stat(devName.data(), &fileStat) != 0) 36 | { 37 | lg2::debug("Ethernet device not found"); 38 | return false; 39 | } 40 | 41 | return true; 42 | } 43 | 44 | bool isValidPrivLimit(const uint8_t privLimit) 45 | { 46 | // Callback privilege is deprecated in OpenBMC 47 | // At present, "OEM Privilege" is not used in OpenBMC 48 | return ((privLimit > PRIVILEGE_CALLBACK) && (privLimit < PRIVILEGE_OEM)); 49 | } 50 | 51 | bool isValidAccessMode(const uint8_t accessMode) 52 | { 53 | return (accessMode <= static_cast(EChannelAccessMode::shared)); 54 | } 55 | 56 | bool isValidChannel(const uint8_t chNum) 57 | { 58 | return getChannelConfigObject().isValidChannel(chNum); 59 | } 60 | 61 | bool isValidAuthType(const uint8_t chNum, const EAuthType& authType) 62 | { 63 | return getChannelConfigObject().isValidAuthType(chNum, authType); 64 | } 65 | 66 | EChannelSessSupported getChannelSessionSupport(const uint8_t chNum) 67 | { 68 | return getChannelConfigObject().getChannelSessionSupport(chNum); 69 | } 70 | 71 | int getChannelActiveSessions(const uint8_t chNum) 72 | { 73 | return getChannelConfigObject().getChannelActiveSessions(chNum); 74 | } 75 | 76 | size_t getChannelMaxTransferSize(uint8_t chNum) 77 | { 78 | return getChannelConfigObject().getChannelMaxTransferSize(chNum); 79 | } 80 | 81 | Cc ipmiChannelInit() 82 | { 83 | getChannelConfigObject(); 84 | getCipherConfigObject(csPrivFileName, csPrivDefaultFileName); 85 | return ccSuccess; 86 | } 87 | 88 | Cc getChannelInfo(const uint8_t chNum, ChannelInfo& chInfo) 89 | { 90 | return getChannelConfigObject().getChannelInfo(chNum, chInfo); 91 | } 92 | 93 | Cc getChannelAccessData(const uint8_t chNum, ChannelAccess& chAccessData) 94 | { 95 | return getChannelConfigObject().getChannelAccessData(chNum, chAccessData); 96 | } 97 | 98 | Cc setChannelAccessData(const uint8_t chNum, const ChannelAccess& chAccessData, 99 | const uint8_t setFlag) 100 | { 101 | return getChannelConfigObject().setChannelAccessData(chNum, chAccessData, 102 | setFlag); 103 | } 104 | 105 | Cc getChannelAccessPersistData(const uint8_t chNum, ChannelAccess& chAccessData) 106 | { 107 | return getChannelConfigObject().getChannelAccessPersistData( 108 | chNum, chAccessData); 109 | } 110 | 111 | Cc setChannelAccessPersistData(const uint8_t chNum, 112 | const ChannelAccess& chAccessData, 113 | const uint8_t setFlag) 114 | { 115 | return getChannelConfigObject().setChannelAccessPersistData( 116 | chNum, chAccessData, setFlag); 117 | } 118 | 119 | Cc getChannelAuthTypeSupported(const uint8_t chNum, uint8_t& authTypeSupported) 120 | { 121 | return getChannelConfigObject().getChannelAuthTypeSupported( 122 | chNum, authTypeSupported); 123 | } 124 | 125 | Cc getChannelEnabledAuthType(const uint8_t chNum, const uint8_t priv, 126 | EAuthType& authType) 127 | { 128 | return getChannelConfigObject().getChannelEnabledAuthType( 129 | chNum, priv, authType); 130 | } 131 | 132 | std::string getChannelName(const uint8_t chNum) 133 | { 134 | return getChannelConfigObject().getChannelName(chNum); 135 | } 136 | 137 | uint8_t getChannelByName(const std::string& chName) 138 | { 139 | return getChannelConfigObject().getChannelByName(chName); 140 | } 141 | 142 | bool isValidPayloadType(const PayloadType payloadType) 143 | { 144 | return ( 145 | payloadType == PayloadType::IPMI || payloadType == PayloadType::SOL || 146 | payloadType == PayloadType::OPEN_SESSION_REQUEST || 147 | payloadType == PayloadType::OPEN_SESSION_RESPONSE || 148 | payloadType == PayloadType::RAKP1 || 149 | payloadType == PayloadType::RAKP2 || 150 | payloadType == PayloadType::RAKP3 || payloadType == PayloadType::RAKP4); 151 | } 152 | } // namespace ipmi 153 | -------------------------------------------------------------------------------- /user_channel/cipher_mgmt.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | // Copyright (c) 2018 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | */ 16 | #pragma once 17 | #include "channel_layer.hpp" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | namespace ipmi 26 | { 27 | static const std::string csPrivDefaultFileName = 28 | "/usr/share/ipmi-providers/cs_privilege_levels.json"; 29 | 30 | static const std::string csPrivFileName = 31 | "/var/lib/ipmi/cs_privilege_levels.json"; 32 | 33 | static const size_t maxCSRecords = 16; 34 | 35 | using ChannelNumCipherIDPair = std::pair; 36 | using privMap = std::map; 37 | 38 | /** @class CipherConfig 39 | * @brief Class to provide cipher suite functionalities 40 | */ 41 | class CipherConfig 42 | { 43 | public: 44 | ~CipherConfig() = default; 45 | explicit CipherConfig(const std::string& csFileName, 46 | const std::string& csDefaultFileName); 47 | CipherConfig() = delete; 48 | 49 | /** @brief function to get cipher suite privileges from config file 50 | * 51 | * @param[in] chNum - channel number for which we want to get cipher suite 52 | * privilege levels 53 | * 54 | * @param[in] csPrivilegeLevels - gets filled by cipher suite privilege 55 | * levels 56 | * 57 | * @return 0 for success, non zero value for failure 58 | */ 59 | ipmi::Cc getCSPrivilegeLevels( 60 | uint8_t chNum, std::array& csPrivilegeLevels); 61 | 62 | /** @brief function to set/update cipher suite privileges in config file 63 | * 64 | * @param[in] chNum - channel number for which we want to update cipher 65 | * suite privilege levels 66 | * 67 | * @param[in] csPrivilegeLevels - cipher suite privilege levels to update 68 | * in config file 69 | * 70 | * @return 0 for success, non zero value for failure 71 | */ 72 | ipmi::Cc setCSPrivilegeLevels( 73 | uint8_t chNum, 74 | const std::array& csPrivilegeLevels); 75 | 76 | private: 77 | std::string cipherSuitePrivFileName, cipherSuiteDefaultPrivFileName; 78 | 79 | privMap csPrivilegeMap; 80 | 81 | /** @brief function to read json config file 82 | * 83 | * @return nlohmann::json object 84 | */ 85 | nlohmann::json readCSPrivilegeLevels(const std::string& csFileName); 86 | 87 | /** @brief function to write json config file 88 | * 89 | * @param[in] jsonData - json object 90 | * 91 | * @return 0 for success, -errno for failure. 92 | */ 93 | int writeCSPrivilegeLevels(const nlohmann::json& jsonData); 94 | 95 | /** @brief convert to cipher suite privilege from string to value 96 | * 97 | * @param[in] value - privilege value 98 | * 99 | * @return cipher suite privilege index 100 | */ 101 | uint4_t convertToPrivLimitIndex(const std::string& value); 102 | 103 | /** @brief function to convert privilege value to string 104 | * 105 | * @param[in] value - privilege value 106 | * 107 | * @return privilege in string 108 | */ 109 | std::string convertToPrivLimitString(const uint4_t& value); 110 | 111 | /** @brief function to load CS Privilege Levels from json file/files to map 112 | * 113 | */ 114 | void loadCSPrivilegesToMap(); 115 | 116 | /** @brief function to update CS privileges map from json object data, 117 | * jsonData 118 | * 119 | */ 120 | void updateCSPrivilegesMap(const nlohmann::json& jsonData); 121 | }; 122 | 123 | /** @brief function to create static CipherConfig object 124 | * 125 | * @param[in] csFileName - user setting cipher suite privilege file name 126 | * @param[in] csDefaultFileName - default cipher suite privilege file name 127 | * 128 | * @return static CipherConfig object 129 | */ 130 | CipherConfig& getCipherConfigObject(const std::string& csFileName, 131 | const std::string& csDefaultFileName); 132 | } // namespace ipmi 133 | -------------------------------------------------------------------------------- /user_channel/file.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | namespace phosphor 7 | { 8 | namespace user 9 | { 10 | 11 | namespace fs = std::filesystem; 12 | 13 | /** @class File 14 | * @brief Responsible for handling file pointer 15 | * Needed by putspent(3) 16 | */ 17 | class File 18 | { 19 | private: 20 | /** @brief handler for operating on file */ 21 | FILE* fp = nullptr; 22 | 23 | /** @brief File name. Needed in the case where the temp 24 | * needs to be removed 25 | */ 26 | const std::string& name; 27 | 28 | /** @brief Should the file be removed at exit */ 29 | bool removeOnExit = false; 30 | 31 | public: 32 | File() = delete; 33 | File(const File&) = delete; 34 | File& operator=(const File&) = delete; 35 | File(File&&) = delete; 36 | File& operator=(File&&) = delete; 37 | 38 | /** @brief Opens file and uses it to do file operation 39 | * 40 | * @param[in] name - File name 41 | * @param[in] mode - File open mode 42 | * @param[in] removeOnExit - File to be removed at exit or no 43 | */ 44 | File(const std::string& name, const std::string& mode, 45 | bool removeOnExit = false) : name(name), removeOnExit(removeOnExit) 46 | { 47 | fp = fopen(name.c_str(), mode.c_str()); 48 | } 49 | 50 | /** @brief Opens file using provided file descriptor 51 | * 52 | * @param[in] fd - File descriptor 53 | * @param[in] name - File name 54 | * @param[in] mode - File open mode 55 | * @param[in] removeOnExit - File to be removed at exit or no 56 | */ 57 | File(int fd, const std::string& name, const std::string& mode, 58 | bool removeOnExit = false) : name(name), removeOnExit(removeOnExit) 59 | { 60 | fp = fdopen(fd, mode.c_str()); 61 | } 62 | 63 | ~File() 64 | { 65 | if (fp) 66 | { 67 | fclose(fp); 68 | } 69 | 70 | // Needed for exception safety 71 | if (removeOnExit && fs::exists(name)) 72 | { 73 | fs::remove(name); 74 | } 75 | } 76 | 77 | auto operator()() 78 | { 79 | return fp; 80 | } 81 | }; 82 | 83 | } // namespace user 84 | } // namespace phosphor 85 | -------------------------------------------------------------------------------- /user_channel/meson.build: -------------------------------------------------------------------------------- 1 | user_channel_inc = include_directories('.') 2 | 3 | channellayer_pre = declare_dependency( 4 | include_directories: [root_inc, user_channel_inc], 5 | dependencies: [ 6 | crypto, 7 | ipmid_dep, 8 | libsystemd_dep, 9 | nlohmann_json_dep, 10 | phosphor_dbus_interfaces_dep, 11 | phosphor_logging_dep, 12 | ], 13 | ) 14 | 15 | channellayer_src = ['channel_layer.cpp', 'channel_mgmt.cpp', 'cipher_mgmt.cpp'] 16 | 17 | channellayer_lib = library( 18 | 'channellayer', 19 | channellayer_src, 20 | implicit_include_directories: false, 21 | dependencies: channellayer_pre, 22 | version: meson.project_version(), 23 | install: true, 24 | install_dir: get_option('libdir'), 25 | override_options: ['b_lundef=false'], 26 | ) 27 | 28 | channellayer_dep = declare_dependency( 29 | link_with: channellayer_lib, 30 | dependencies: channellayer_pre, 31 | ) 32 | 33 | import('pkgconfig').generate( 34 | channellayer_lib, 35 | name: 'libchannellayer', 36 | version: meson.project_version(), 37 | description: 'libchannellayer', 38 | ) 39 | 40 | if get_option('libuserlayer').allowed() 41 | userlayer_pre = declare_dependency( 42 | include_directories: [root_inc, user_channel_inc], 43 | dependencies: [ 44 | channellayer_dep, 45 | crypto, 46 | ipmid_dep, 47 | libsystemd_dep, 48 | nlohmann_json_dep, 49 | pam, 50 | phosphor_dbus_interfaces_dep, 51 | phosphor_logging_dep, 52 | ], 53 | ) 54 | 55 | userlayer_src = ['user_layer.cpp', 'user_mgmt.cpp', 'passwd_mgr.cpp'] 56 | 57 | userlayer_lib = library( 58 | 'userlayer', 59 | userlayer_src, 60 | implicit_include_directories: false, 61 | dependencies: userlayer_pre, 62 | version: meson.project_version(), 63 | install: true, 64 | install_dir: get_option('libdir'), 65 | override_options: ['b_lundef=false'], 66 | ) 67 | 68 | userlayer_dep = declare_dependency( 69 | link_with: userlayer_lib, 70 | dependencies: userlayer_pre, 71 | ) 72 | 73 | usercmds_pre = declare_dependency( 74 | include_directories: [root_inc, user_channel_inc], 75 | dependencies: [ 76 | phosphor_logging_dep, 77 | ipmid_dep, 78 | userlayer_dep, 79 | channellayer_dep, 80 | ], 81 | ) 82 | 83 | usercmds_lib = library( 84 | 'usercmds', 85 | 'usercommands.cpp', 86 | implicit_include_directories: false, 87 | dependencies: usercmds_pre, 88 | install: true, 89 | install_dir: get_option('libdir') / 'ipmid-providers', 90 | version: meson.project_version(), 91 | override_options: ['b_lundef=false'], 92 | ) 93 | 94 | import('pkgconfig').generate( 95 | userlayer_lib, 96 | name: 'libuserlayer', 97 | version: meson.project_version(), 98 | description: 'libuserlayer', 99 | ) 100 | 101 | endif 102 | -------------------------------------------------------------------------------- /user_channel/passwd_mgr.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | // Copyright (c) 2018 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | */ 16 | #pragma once 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace ipmi 27 | { 28 | 29 | class PasswdMgr 30 | { 31 | public: 32 | ~PasswdMgr() = default; 33 | PasswdMgr(const PasswdMgr&) = delete; 34 | PasswdMgr& operator=(const PasswdMgr&) = delete; 35 | PasswdMgr(PasswdMgr&&) = delete; 36 | PasswdMgr& operator=(PasswdMgr&&) = delete; 37 | 38 | /** @brief Constructs user password list 39 | * 40 | */ 41 | PasswdMgr(); 42 | 43 | /** @brief Get password for the user 44 | * 45 | * @param[in] userName - user name 46 | * 47 | * @return password string. will return empty string, if unable to locate 48 | * the user 49 | */ 50 | SecureString getPasswdByUserName(const std::string& userName); 51 | 52 | /** @brief Update / clear username and password entry for the specified 53 | * user 54 | * 55 | * @param[in] userName - user name that has to be renamed / deleted 56 | * @param[in] newUserName - new user name. If empty, userName will be 57 | * deleted. 58 | * 59 | * @return error response 60 | */ 61 | int updateUserEntry(const std::string& userName, 62 | const std::string& newUserName); 63 | 64 | private: 65 | using UserName = std::string; 66 | using Password = SecureString; 67 | std::unordered_map passwdMapList; 68 | std::time_t fileLastUpdatedTime; 69 | 70 | /** @brief restrict file permission 71 | * 72 | */ 73 | void restrictFilesPermission(void); 74 | /** @brief check timestamp and reload password map if required 75 | * 76 | */ 77 | void checkAndReload(void); 78 | /** @brief initializes passwdMapList by reading the encrypted file 79 | * 80 | * Initializes the passwordMapList members after decrypting the 81 | * password file. passwordMapList will be used further in IPMI 82 | * authentication. 83 | */ 84 | void initPasswordMap(void); 85 | 86 | /** @brief Function to read the encrypted password file data 87 | * 88 | * @param[out] outBytes - vector to hold decrypted password file data 89 | * 90 | * @return error response 91 | */ 92 | int readPasswdFileData(SecureString& outBytes); 93 | /** @brief Updates special password file by clearing the password entry 94 | * for the user specified. 95 | * 96 | * @param[in] userName - user name that has to be renamed / deleted 97 | * @param[in] newUserName - new user name. If empty, userName will be 98 | * deleted. 99 | * 100 | * @return error response 101 | */ 102 | int updatePasswdSpecialFile(const std::string& userName, 103 | const std::string& newUserName); 104 | /** @brief encrypts or decrypt the data provided 105 | * 106 | * @param[in] doEncrypt - do encrypt if set to true, else do decrypt. 107 | * @param[in] cipher - cipher to be used 108 | * @param[in] key - pointer to the key 109 | * @param[in] keyLen - Length of the key to be used 110 | * @param[in] iv - pointer to initialization vector 111 | * @param[in] ivLen - Length of the iv 112 | * @param[in] inBytes - input data to be encrypted / decrypted 113 | * @param[in] inBytesLen - input size to be encrypted / decrypted 114 | * @param[in] mac - message authentication code - to figure out corruption 115 | * @param[in] macLen - size of MAC 116 | * @param[in] outBytes - ptr to store output bytes 117 | * @param[in] outBytesLen - outbut data length. 118 | * 119 | * @return error response 120 | */ 121 | int encryptDecryptData( 122 | bool doEncrypt, const EVP_CIPHER* cipher, uint8_t* key, size_t keyLen, 123 | uint8_t* iv, size_t ivLen, uint8_t* inBytes, size_t inBytesLen, 124 | uint8_t* mac, size_t* macLen, uint8_t* outBytes, size_t* outBytesLen); 125 | 126 | /** @brief returns updated file time of passwd file entry. 127 | * 128 | * @return timestamp or -1 for error. 129 | */ 130 | std::time_t getUpdatedFileTime(); 131 | }; 132 | 133 | } // namespace ipmi 134 | -------------------------------------------------------------------------------- /user_channel/shadowlock.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | namespace phosphor 9 | { 10 | namespace user 11 | { 12 | namespace shadow 13 | { 14 | 15 | using InternalFailure = 16 | sdbusplus::error::xyz::openbmc_project::common::InternalFailure; 17 | using namespace phosphor::logging; 18 | 19 | /** @class Lock 20 | * @brief Responsible for locking and unlocking /etc/shadow 21 | */ 22 | class Lock 23 | { 24 | public: 25 | Lock(const Lock&) = delete; 26 | Lock& operator=(const Lock&) = delete; 27 | Lock(Lock&&) = delete; 28 | Lock& operator=(Lock&&) = delete; 29 | 30 | /** @brief Default constructor that just locks the shadow file */ 31 | Lock() 32 | { 33 | if (!lckpwdf()) 34 | { 35 | lg2::error("Locking Shadow failed"); 36 | elog(); 37 | } 38 | } 39 | ~Lock() 40 | { 41 | if (!ulckpwdf()) 42 | { 43 | lg2::error("Un-Locking Shadow failed"); 44 | elog(); 45 | } 46 | } 47 | }; 48 | 49 | } // namespace shadow 50 | } // namespace user 51 | } // namespace phosphor 52 | -------------------------------------------------------------------------------- /user_channel/usercommands.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | // Copyright (c) 2018 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | #include 19 | 20 | namespace ipmi 21 | { 22 | 23 | /** 24 | * @enum IPMI set password return codes (refer spec sec 22.30) 25 | */ 26 | enum ipmi_set_password_return_codes : uint8_t 27 | { 28 | ipmiCCPasswdFailMismatch = 0x80, 29 | ipmiCCPasswdFailWrongSize = 0x81, 30 | }; 31 | 32 | static constexpr uint8_t userIdEnabledViaSetPassword = 0x1; 33 | static constexpr uint8_t userIdDisabledViaSetPassword = 0x2; 34 | 35 | void registerUserIpmiFunctions(); 36 | } // namespace ipmi 37 | -------------------------------------------------------------------------------- /xyz/openbmc_project/Ipmi/Internal/SoftPowerOff.interface.yaml: -------------------------------------------------------------------------------- 1 | description: > 2 | Implement the Soft Power Off function. On receiving the SMS_ATTN from BMC, 3 | Host will respond with a GetMessageFlags command and the BMC will respond 4 | with a static data indicating that Event Message Buffer is full. Host then 5 | sends 'ReadEvent' command and BMC responds with an architected packet 6 | mentioning that the type is SOFT_OFF. Host then goes ahead and starts to 7 | quiesce. Once that is done, Host will send a hard power off command to BMC 8 | and then BMC will issue a hard power off. 9 | 10 | properties: 11 | - name: ResponseReceived 12 | type: enum[self.HostResponse] 13 | default: NotApplicable 14 | description: > 15 | When the response is received for 'SMS_ATN', this is set to 16 | 'SoftOffReceived' and is set to 'HostShutdown' when Host sends a Power 17 | Off request. 18 | 19 | enumerations: 20 | - name: HostResponse 21 | description: > 22 | Possible response types from Host for a Soft Power Off function. 23 | values: 24 | - name: NotApplicable 25 | description: > 26 | Default initial value. 27 | - name: SoftOffReceived 28 | description: > 29 | Host has received the SMS_ATN from BMC indicating that Host 30 | needs to do a Soft Power Off. 31 | - name: HostShutdown 32 | description: > 33 | Host has sufficiently quiesced and acknowledged the shutdown 34 | request such that the hardware shutdown sequence can safely be 35 | performed. 36 | -------------------------------------------------------------------------------- /xyz/openbmc_project/Ipmi/Internal/SoftPowerOff/meson.build: -------------------------------------------------------------------------------- 1 | sdbuspp_prog = find_program('sdbus++') 2 | 3 | domain = 'xyz.openbmc_project.Ipmi.Internal.SoftPowerOff' 4 | if_yaml_file = files('../SoftPowerOff.interface.yaml') 5 | 6 | if_cpp = custom_target( 7 | 'server.cpp', 8 | output: 'server.cpp', 9 | input: if_yaml_file, 10 | capture: true, 11 | command: [sdbuspp_prog, '-r', root, 'interface', 'server-cpp', domain], 12 | ) 13 | 14 | if_hpp = custom_target( 15 | 'server.hpp', 16 | output: 'server.hpp', 17 | input: if_yaml_file, 18 | capture: true, 19 | command: [sdbuspp_prog, '-r', root, 'interface', 'server-header', domain], 20 | install: true, 21 | install_dir: get_option('includedir') / 'xyz/openbmc_project/Ipmi/Internal/SoftPowerOff', 22 | ) 23 | 24 | if_common_hpp = custom_target( 25 | 'common.hpp', 26 | output: 'common.hpp', 27 | input: if_yaml_file, 28 | capture: true, 29 | command: [sdbuspp_prog, '-r', root, 'interface', 'common-header', domain], 30 | install: true, 31 | install_dir: get_option('includedir') / 'xyz/openbmc_project/Ipmi/Internal/SoftPowerOff', 32 | ) 33 | 34 | softoff_dbus_deps = [ 35 | dependency('phosphor-dbus-interfaces'), 36 | dependency('sdbusplus'), 37 | ] 38 | 39 | softoff_dbus_lib = library( 40 | 'softoff-dbus', 41 | [if_cpp, if_hpp, if_common_hpp], 42 | implicit_include_directories: false, 43 | include_directories: root_inc, 44 | version: meson.project_version(), 45 | dependencies: softoff_dbus_deps, 46 | override_options: ['b_lundef=false'], 47 | install: true, 48 | ) 49 | 50 | softoff_dbus = declare_dependency( 51 | dependencies: softoff_dbus_deps, 52 | sources: [if_hpp, if_common_hpp], 53 | link_with: softoff_dbus_lib, 54 | ) 55 | 56 | softoff_dbus_reqs = [] 57 | foreach dep : softoff_dbus_deps 58 | if dep.type_name() == 'pkgconfig' 59 | softoff_dbus_reqs += dep 60 | endif 61 | endforeach 62 | 63 | import('pkgconfig').generate( 64 | name: 'softoff-dbus', 65 | description: 'SoftPowerOff DBus Bindings', 66 | version: meson.project_version(), 67 | libraries: softoff_dbus, 68 | requires: softoff_dbus_reqs, 69 | ) 70 | --------------------------------------------------------------------------------