summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--MAINTAINERS1
-rw-r--r--Makefile.am32
-rw-r--r--app/channel.cpp87
-rw-r--r--app/channel.hpp58
-rw-r--r--app/watchdog.cpp208
-rw-r--r--app/watchdog.hpp61
-rw-r--r--app/watchdog_service.cpp37
-rw-r--r--app/watchdog_service.hpp15
-rw-r--r--apphandler.cpp1362
-rw-r--r--apphandler.hpp1
-rw-r--r--chassishandler.cpp964
-rw-r--r--chassishandler.hpp10
-rw-r--r--configure.ac86
-rw-r--r--dcmihandler.cpp155
-rw-r--r--dcmihandler.hpp86
-rw-r--r--docs/configuration.md2
-rw-r--r--docs/contributing.md122
-rw-r--r--docs/testing.md471
-rw-r--r--entity_map_json.cpp119
-rw-r--r--entity_map_json.hpp57
-rw-r--r--globalhandler.cpp25
-rw-r--r--globalhandler.hpp1
-rw-r--r--host-cmd-manager.cpp3
-rw-r--r--host-interface.cpp2
-rw-r--r--host-ipmid-whitelist.conf2
-rw-r--r--include/Makefile.am2
-rw-r--r--include/ipmid/api.hpp2
-rw-r--r--include/ipmid/handler.hpp93
-rw-r--r--include/ipmid/message.hpp103
-rw-r--r--include/ipmid/message/pack.hpp37
-rw-r--r--include/ipmid/message/unpack.hpp54
-rw-r--r--include/ipmid/sessiondef.hpp52
-rw-r--r--include/ipmid/sessionhelper.hpp88
-rw-r--r--include/ipmid/types.hpp37
-rw-r--r--include/ipmid/utility.hpp7
-rw-r--r--include/ipmid/utils.hpp77
-rw-r--r--ipmi_fru_info_area.cpp37
-rw-r--r--ipmid-new.cpp370
-rw-r--r--libipmid/utils.cpp176
-rw-r--r--oemrouter.cpp152
-rw-r--r--read_fru_data.cpp40
-rw-r--r--[-rwxr-xr-x]scripts/entity-example.md (renamed from scripts/entity-example.yaml)26
-rwxr-xr-xscripts/entity_gen.py60
-rwxr-xr-xscripts/fru_gen.py4
-rw-r--r--[-rwxr-xr-x]scripts/inventory-sensor-example.yaml0
-rwxr-xr-xscripts/inventory-sensor.py4
-rw-r--r--[-rwxr-xr-x]scripts/sensor-example.yaml38
-rwxr-xr-xscripts/sensor_gen.py4
-rw-r--r--scripts/writeentity.mako.cpp34
-rw-r--r--scripts/writesensor.mako.cpp82
-rw-r--r--selutility.cpp21
-rw-r--r--selutility.hpp51
-rw-r--r--sensordatahandler.cpp29
-rw-r--r--sensordatahandler.hpp47
-rw-r--r--sensorhandler.cpp574
-rw-r--r--sensorhandler.hpp67
-rw-r--r--settings.cpp5
-rw-r--r--storagehandler.cpp577
-rw-r--r--storagehandler.hpp66
-rw-r--r--systemintfcmds.cpp113
-rw-r--r--test/Makefile.am23
-rw-r--r--test/entitymap_json_unittest.cpp223
-rw-r--r--test/message/pack.cpp71
-rw-r--r--test/message/payload.cpp134
-rw-r--r--test/message/unpack.cpp51
-rw-r--r--test/session/closesession_unittest.cpp114
-rw-r--r--transporthandler.cpp2516
-rw-r--r--transporthandler.hpp138
-rw-r--r--user_channel/Makefile.am9
-rw-r--r--user_channel/channel_layer.cpp20
-rw-r--r--user_channel/channel_layer.hpp35
-rw-r--r--user_channel/channel_mgmt.cpp142
-rw-r--r--user_channel/channel_mgmt.hpp24
-rw-r--r--user_channel/channelcommands.cpp591
-rw-r--r--user_channel/channelcommands.hpp35
-rw-r--r--user_channel/passwd_mgr.cpp7
-rw-r--r--user_channel/user_layer.cpp59
-rw-r--r--user_channel/user_layer.hpp62
-rw-r--r--user_channel/user_mgmt.cpp495
-rw-r--r--user_channel/user_mgmt.hpp86
-rw-r--r--user_channel/usercommands.cpp207
82 files changed, 7856 insertions, 4288 deletions
diff --git a/.gitignore b/.gitignore
index 288aef3..bc496d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,14 +66,14 @@ Makefile
/entity-gen.cpp
# test related
-/test/sample_unittest
/test/*.log
/test/*.trs
/test-suite.log
*-1.0-coverage*
-test/*.gcda
-test/*.gcno
+*.gcda
+*.gcno
softoff/test
+test/*_unittest
# ignore vim swap files
.*.sw*
diff --git a/MAINTAINERS b/MAINTAINERS
index 7849e57..b4f1e57 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -64,7 +64,6 @@ START OF MAINTAINERS LIST
M: Vernon Mauery <vernon.mauery@linux.intel.com> <vmauery!>
M: Tom Joseph <tomjose@linux.vnet.ibm.com> <tomjoseph!>
-M: Emily Shaffer <emilyshaffer@google.com> <nasamuffin!>
R: Adriana Kobylak <anoo@us.ibm.com> <anoo!>
R: Deepak Kodihalli <dkodihal@linux.vnet.ibm.com> <dkodihal!>
R: Ratan Gupta <ratagupt@linux.vnet.ibm.com> <rgupta!>
diff --git a/Makefile.am b/Makefile.am
index 864eb93..82fdd5d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,6 +14,12 @@ endif
providersdir = ${libdir}/ipmid-providers
providers_LTLIBRARIES =
+if FEATURE_IPMI_WHITELIST
+IPMI_WHITELIST_SOURCE = ipmiwhitelist.cpp
+else
+IPMI_WHITELIST_SOURCE =
+endif
+
bin_PROGRAMS = \
ipmid
@@ -25,11 +31,10 @@ ipmid_SOURCES = \
libipmi20_BUILT_LIST = \
sensor-gen.cpp \
inventory-sensor-gen.cpp \
- fru-read-gen.cpp \
- entity-gen.cpp
+ fru-read-gen.cpp
BUILT_SOURCES = \
- ipmiwhitelist.cpp \
+ $(IPMI_WHITELIST_SOURCE) \
$(libipmi20_BUILT_LIST)
CLEANFILES = $(BUILT_SOURCES)
@@ -70,19 +75,22 @@ ipmid_LDFLAGS = \
ipmiwhitelist.cpp: ${srcdir}/generate_whitelist.sh $(WHITELIST_CONF)
$(SHELL) $^ > $@
-sensor-gen.cpp:
+sensor-gen.cpp: scripts/writesensor.mako.cpp scripts/sensor_gen.py @SENSOR_YAML_GEN@
$(AM_V_GEN)@SENSORGEN@ -o $(top_builddir) generate-cpp
-inventory-sensor-gen.cpp:
+inventory-sensor-gen.cpp: scripts/inventorysensor.mako.cpp scripts/inventory-sensor.py @INVSENSOR_YAML_GEN@
$(AM_V_GEN)@INVSENSORGEN@ -o $(top_builddir) generate-cpp
-fru-read-gen.cpp:
+fru-read-gen.cpp: scripts/readfru.mako.cpp scripts/fru_gen.py @FRU_YAML_GEN@
$(AM_V_GEN)@FRUGEN@ -o $(top_builddir) generate-cpp
-entity-gen.cpp:
- $(AM_V_GEN)@ENTITYGEN@ -o $(top_builddir) generate-cpp
-
providers_LTLIBRARIES += libipmi20.la
+if FEATURE_TRANSPORT_OEM
+libipmi20_la_TRANSPORTOEM = transporthandler_oem.cpp
+else
+libipmi20_la_TRANSPORTOEM =
+endif
+
libipmi20_la_SOURCES = \
app/channel.cpp \
app/watchdog.cpp \
@@ -90,6 +98,7 @@ libipmi20_la_SOURCES = \
apphandler.cpp \
sys_info_param.cpp \
sensorhandler.cpp \
+ entity_map_json.cpp \
storagehandler.cpp \
chassishandler.cpp \
dcmihandler.cpp \
@@ -103,6 +112,7 @@ libipmi20_la_SOURCES = \
read_fru_data.cpp \
sensordatahandler.cpp \
user_channel/channelcommands.cpp \
+ $(libipmi20_la_TRANSPORTOEM) \
$(libipmi20_BUILT_LIST)
check_PROGRAMS =
@@ -123,6 +133,7 @@ libipmi20_la_LDFLAGS = \
-version-info 0:0:0 -shared
libipmi20_la_CXXFLAGS = $(COMMON_CXX)
+if FEATURE_LIBUSERLAYER
providers_LTLIBRARIES += libusercmds.la
libusercmds_la_LIBADD = \
libipmid/libipmid.la \
@@ -135,6 +146,7 @@ libusercmds_la_LDFLAGS = \
$(libmapper_LIBS) \
-version-info 0:0:0 -shared
libusercmds_la_CXXFLAGS = $(COMMON_CXX)
+endif
providers_LTLIBRARIES += libsysintfcmds.la
libsysintfcmds_la_LIBADD = \
@@ -152,6 +164,7 @@ libsysintfcmds_la_LDFLAGS = \
-version-info 0:0:0 -shared
libsysintfcmds_la_CXXFLAGS = $(COMMON_CXX)
+if FEATURE_IPMI_WHITELIST
libwhitelistdir = ${libdir}/ipmid-providers
libwhitelist_LTLIBRARIES = libwhitelist.la
libwhitelist_la_SOURCES = \
@@ -164,6 +177,7 @@ libwhitelist_la_LDFLAGS = \
-version-info 0:0:0 -shared
libwhitelist_la_CXXFLAGS = $(COMMON_CXX)
nodist_libwhitelist_la_SOURCES = ipmiwhitelist.cpp
+endif
nobase_include_HEADERS = \
user_channel/channel_layer.hpp \
diff --git a/app/channel.cpp b/app/channel.cpp
index 6ea25c4..51e953d 100644
--- a/app/channel.cpp
+++ b/app/channel.cpp
@@ -1,6 +1,5 @@
#include "channel.hpp"
-#include "transporthandler.hpp"
#include "user_channel/channel_layer.hpp"
#include <arpa/inet.h>
@@ -97,26 +96,44 @@ std::pair<std::vector<uint8_t>, std::vector<uint8_t>> getCipherRecords()
} // namespace cipher
-ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief this command is used to look up what authentication, integrity,
+ * confidentiality algorithms are supported.
+ *
+ * @ param ctx - context pointer
+ * @ param channelNumber - channel number
+ * @ param payloadType - payload type
+ * @ param listIndex - list index
+ * @ param algoSelectBit - list algorithms
+ *
+ * @returns ipmi completion code plus response data
+ * - rspChannel - channel number for authentication algorithm.
+ * - rspRecords - cipher suite records.
+ **/
+ipmi::RspType<uint8_t, // Channel Number
+ std::vector<uint8_t> // Cipher Records
+ >
+ getChannelCipherSuites(ipmi::Context::ptr ctx, uint4_t channelNumber,
+ uint4_t reserved1, uint8_t payloadType,
+ uint6_t listIndex, uint1_t reserved2,
+ uint1_t algoSelectBit)
{
static std::vector<uint8_t> cipherRecords;
static std::vector<uint8_t> supportedAlgorithms;
static auto recordInit = false;
- auto requestData =
- reinterpret_cast<const GetChannelCipherRequest*>(request);
+ uint8_t rspChannel = ipmi::convertCurrentChannelNum(
+ static_cast<uint8_t>(channelNumber), ctx->channel);
- if (*data_len < sizeof(GetChannelCipherRequest))
+ if (!ipmi::isValidChannel(rspChannel) || reserved1 != 0 || reserved2 != 0)
{
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
+ return ipmi::responseInvalidFieldRequest();
+ }
+ if (!ipmi::isValidPayloadType(static_cast<ipmi::PayloadType>(payloadType)))
+ {
+ log<level::DEBUG>("Get channel cipher suites - Invalid payload type");
+ constexpr uint8_t ccPayloadTypeNotSupported = 0x80;
+ return ipmi::response(ccPayloadTypeNotSupported);
}
-
- *data_len = 0;
if (!recordInit)
{
@@ -128,41 +145,37 @@ ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
}
catch (const std::exception& e)
{
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
}
- const auto& records = (cipher::listCipherSuite ==
- (requestData->listIndex & cipher::listTypeMask))
- ? cipherRecords
- : supportedAlgorithms;
+ const std::vector<uint8_t>& records =
+ algoSelectBit ? cipherRecords : supportedAlgorithms;
+ static constexpr auto respSize = 16;
+
+ // Session support is available in active LAN channels.
+ if ((ipmi::getChannelSessionSupport(rspChannel) ==
+ ipmi::EChannelSessSupported::none) ||
+ !(ipmi::doesDeviceExist(rspChannel)))
+ {
+ log<level::DEBUG>("Get channel cipher suites - Device does not exist");
+ return ipmi::responseInvalidFieldRequest();
+ }
// List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
// set of 16 and so on.
- auto index =
- static_cast<size_t>(requestData->listIndex & cipher::listIndexMask);
// Calculate the number of record data bytes to be returned.
- auto start = std::min(index * cipher::respSize, records.size());
- auto end =
- std::min((index * cipher::respSize) + cipher::respSize, records.size());
+ auto start =
+ std::min(static_cast<size_t>(listIndex) * respSize, records.size());
+ auto end = std::min((static_cast<size_t>(listIndex) * respSize) + respSize,
+ records.size());
auto size = end - start;
- auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>(response);
- responseData->channelNumber = cipher::defaultChannelNumber;
-
- if (!size)
- {
- *data_len = sizeof(GetChannelCipherRespHeader);
- }
- else
- {
- std::copy_n(records.data() + start, size,
- static_cast<uint8_t*>(response) + 1);
- *data_len = size + sizeof(GetChannelCipherRespHeader);
- }
+ std::vector<uint8_t> rspRecords;
+ std::copy_n(records.data() + start, size, std::back_inserter(rspRecords));
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(rspChannel, rspRecords);
}
template <typename... ArgTypes>
diff --git a/app/channel.hpp b/app/channel.hpp
index 7004ddd..8e5accb 100644
--- a/app/channel.hpp
+++ b/app/channel.hpp
@@ -1,6 +1,6 @@
#include "nlohmann/json.hpp"
-#include <ipmid/api.h>
+#include <ipmid/api.hpp>
/** @brief The set channel access IPMI command.
*
@@ -53,31 +53,31 @@ ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_data_len_t data_len,
ipmi_context_t context);
-/** @brief Implementation of get channel cipher suites command
+/** @brief this command is used to look up what authentication, integrity,
+ * confidentiality algorithms are supported.
*
- * @param[in] netfn - Net Function
- * @param[in] cmd - Command
- * @param[in] request - Request pointer
- * @param[in,out] response - Response pointer
- * @param[in,out] data_len - Data Length
- * @param[in] context - Context
+ * @ param ctx - context pointer
+ * @ param channelNumber - channel number
+ * @ param payloadType - payload type
+ * @ param listIndex - list index
+ * @ param algoSelectBit - list algorithms
*
- * @return IPMI_CC_OK on success, non-zero otherwise.
- */
-ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context);
+ * @returns ipmi completion code plus response data
+ * - rspChannel - channel number for authentication algorithm.
+ * - rspRecords - cipher suite records.
+ **/
+ipmi::RspType<uint8_t, // Channel Number
+ std::vector<uint8_t> // Cipher Records
+ >
+ getChannelCipherSuites(ipmi::Context::ptr ctx, uint4_t channelNumber,
+ uint4_t reserved1, uint8_t payloadType,
+ uint6_t listIndex, uint1_t reserved2,
+ uint1_t algoSelectBit);
namespace cipher
{
-static constexpr auto defaultChannelNumber = 1;
-static constexpr auto listTypeMask = 0x80;
static constexpr auto listCipherSuite = 0x80;
-static constexpr auto listIndexMask = 0x3F;
-static constexpr auto respSize = 16;
using Json = nlohmann::json;
static constexpr auto configFile = "/usr/share/ipmi-providers/cipher_list.json";
@@ -92,23 +92,3 @@ static constexpr auto conf = "confidentiality";
static constexpr auto confTag = 0x80;
} // namespace cipher
-
-/** @struct GetChannelCipherRequest
- *
- * IPMI payload for Get Channel Cipher Suites command request
- */
-struct GetChannelCipherRequest
-{
- uint8_t channelNumber; //!< Channel Number
- uint8_t payloadType; //!< Payload type number
- uint8_t listIndex; //!< List Index
-} __attribute__((packed));
-
-/** @struct GetChannelCipherRespHeader
- *
- * IPMI payload for Get Channel Cipher Suites command response header
- */
-struct GetChannelCipherRespHeader
-{
- uint8_t channelNumber; //!< Channel Number
-} __attribute__((packed));
diff --git a/app/watchdog.cpp b/app/watchdog.cpp
index b1eea1c..03c373e 100644
--- a/app/watchdog.cpp
+++ b/app/watchdog.cpp
@@ -4,6 +4,7 @@
#include <endian.h>
+#include <bitset>
#include <cstdint>
#include <ipmid/api.hpp>
#include <phosphor-logging/elog-errors.hpp>
@@ -68,13 +69,11 @@ ipmi::RspType<> ipmiAppResetWatchdogTimer()
{
const std::string e_str = std::string("wd_reset: ") + e.what();
log<level::ERR>(e_str.c_str());
- reportError();
return ipmi::responseUnspecifiedError();
}
catch (...)
{
log<level::ERR>("wd_reset: Unknown Error");
- reportError();
return ipmi::responseUnspecifiedError();
}
}
@@ -82,7 +81,12 @@ ipmi::RspType<> ipmiAppResetWatchdogTimer()
static constexpr uint8_t wd_dont_stop = 0x1 << 6;
static constexpr uint8_t wd_timeout_action_mask = 0x3;
-static constexpr uint8_t wdTimerUseMask = 0x7;
+static constexpr uint8_t wdTimerUseResTimer1 = 0x0;
+static constexpr uint8_t wdTimerUseResTimer2 = 0x6;
+static constexpr uint8_t wdTimerUseResTimer3 = 0x7;
+
+static constexpr uint8_t wdTimeoutActionMax = 3;
+static constexpr uint8_t wdTimeoutInterruptTimer = 0x04;
enum class IpmiAction : uint8_t
{
@@ -168,85 +172,106 @@ WatchdogService::TimerUse ipmiTimerUseToWdTimerUse(IpmiTimerUse ipmiTimerUse)
}
}
-struct wd_set_req
-{
- uint8_t timer_use;
- uint8_t timer_action;
- uint8_t pretimeout; // (seconds)
- uint8_t expire_flags;
- uint16_t initial_countdown; // Little Endian (deciseconds)
-} __attribute__((packed));
-static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
-static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
- "wd_get_res can't fit in request buffer.");
-
-ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+static bool timerNotLogFlags = false;
+static std::bitset<8> timerUseExpirationFlags = 0;
+static uint3_t timerPreTimeoutInterrupt = 0;
+static constexpr uint8_t wdExpirationFlagReservedBit0 = 0x0;
+static constexpr uint8_t wdExpirationFlagReservedBit6 = 0x6;
+static constexpr uint8_t wdExpirationFlagReservedBit7 = 0x7;
+
+/**@brief The Set Watchdog Timer ipmi command.
+ *
+ * @param
+ * - timerUse
+ * - dontStopTimer
+ * - dontLog
+ * - timerAction
+ * - pretimeout
+ * - expireFlags
+ * - initialCountdown
+ *
+ * @return completion code on success.
+ **/
+ipmi::RspType<>
+ ipmiSetWatchdogTimer(uint3_t timerUse, uint3_t reserved, bool dontStopTimer,
+ bool dontLog, uint3_t timeoutAction, uint1_t reserved1,
+ uint3_t preTimeoutInterrupt, uint1_t reserved2,
+ uint8_t preTimeoutInterval,
+ std::bitset<8> expFlagValue, uint16_t initialCountdown)
{
- // Extract the request data
- if (*data_len < sizeof(wd_set_req))
+ if ((timerUse == wdTimerUseResTimer1) ||
+ (timerUse == wdTimerUseResTimer2) ||
+ (timerUse == wdTimerUseResTimer3) ||
+ (timeoutAction > wdTimeoutActionMax) ||
+ (preTimeoutInterrupt == wdTimeoutInterruptTimer) ||
+ (reserved | reserved1 | reserved2 |
+ expFlagValue.test(wdExpirationFlagReservedBit0) |
+ expFlagValue.test(wdExpirationFlagReservedBit6) |
+ expFlagValue.test(wdExpirationFlagReservedBit7)))
{
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
+ return ipmi::responseInvalidFieldRequest();
}
- wd_set_req req;
- memcpy(&req, request, sizeof(req));
- req.initial_countdown = le16toh(req.initial_countdown);
- *data_len = 0;
+
+ if (preTimeoutInterval > (initialCountdown / 10))
+ {
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ timerNotLogFlags = dontLog;
+ timerPreTimeoutInterrupt = preTimeoutInterrupt;
try
{
WatchdogService wd_service;
// Stop the timer if the don't stop bit is not set
- if (!(req.timer_use & wd_dont_stop))
+ if (!(dontStopTimer))
{
wd_service.setEnabled(false);
}
// Set the action based on the request
- const auto ipmi_action =
- static_cast<IpmiAction>(req.timer_action & wd_timeout_action_mask);
+ const auto ipmi_action = static_cast<IpmiAction>(
+ static_cast<uint8_t>(timeoutAction) & wd_timeout_action_mask);
wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
const auto ipmiTimerUse =
- static_cast<IpmiTimerUse>(req.timer_use & wdTimerUseMask);
+ static_cast<IpmiTimerUse>(static_cast<uint8_t>(timerUse));
wd_service.setTimerUse(ipmiTimerUseToWdTimerUse(ipmiTimerUse));
+ wd_service.setExpiredTimerUse(WatchdogService::TimerUse::Reserved);
+
+ timerUseExpirationFlags &= ~expFlagValue;
+
// Set the new interval and the time remaining deci -> mill seconds
- const uint64_t interval = req.initial_countdown * 100;
+ const uint64_t interval = initialCountdown * 100;
wd_service.setInterval(interval);
- wd_service.setTimeRemaining(interval);
+ wd_service.resetTimeRemaining(false);
// Mark as initialized so that future resets behave correctly
wd_service.setInitialized(true);
lastCallSuccessful = true;
- return IPMI_CC_OK;
+ return ipmi::responseSuccess();
}
catch (const std::domain_error&)
{
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return ipmi::responseInvalidFieldRequest();
}
catch (const InternalFailure& e)
{
reportError();
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
catch (const std::exception& e)
{
const std::string e_str = std::string("wd_set: ") + e.what();
log<level::ERR>(e_str.c_str());
- reportError();
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
catch (...)
{
log<level::ERR>("wd_set: Unknown Error");
- reportError();
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
}
@@ -320,30 +345,37 @@ IpmiTimerUse wdTimerUseToIpmiTimerUse(WatchdogService::TimerUse wdTimerUse)
}
}
-struct wd_get_res
-{
- uint8_t timer_use;
- uint8_t timer_action;
- uint8_t pretimeout;
- uint8_t expire_flags;
- uint16_t initial_countdown; // Little Endian (deciseconds)
- uint16_t present_countdown; // Little Endian (deciseconds)
-} __attribute__((packed));
-static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
-static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
- "wd_get_res can't fit in response buffer.");
-
-static constexpr uint8_t wd_dont_log = 0x1 << 7;
static constexpr uint8_t wd_running = 0x1 << 6;
-ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/**@brief The getWatchdogTimer ipmi command.
+ *
+ * @return Completion code plus timer details.
+ * - timerUse
+ * - timerAction
+ * - pretimeout
+ * - expireFlags
+ * - initialCountdown
+ * - presentCountdown
+ **/
+ipmi::RspType<uint3_t, // timerUse - timer use
+ uint3_t, // timerUse - reserved
+ bool, // timerUse - timer is started
+ bool, // timerUse - don't log
+
+ uint3_t, // timerAction - timeout action
+ uint1_t, // timerAction - reserved
+ uint3_t, // timerAction - pre-timeout interrupt
+ uint1_t, // timerAction - reserved
+
+ uint8_t, // pretimeout
+ std::bitset<8>, // expireFlags
+ uint16_t, // initial Countdown - Little Endian (deciseconds)
+ uint16_t // present Countdown - Little Endian (deciseconds)
+ >
+ ipmiGetWatchdogTimer()
{
- // Assume we will fail and send no data outside the return code
- *data_len = 0;
+ uint16_t presentCountdown = 0;
+ uint8_t pretimeout = 0;
try
{
@@ -351,50 +383,58 @@ ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
WatchdogService::Properties wd_prop = wd_service.getProperties();
// Build and return the response
- wd_get_res res;
- res.timer_use = wd_dont_log;
- res.timer_action =
- static_cast<uint8_t>(wdActionToIpmiAction(wd_prop.expireAction));
-
// Interval and timeRemaining need converted from milli -> deci seconds
- res.initial_countdown = htole16(wd_prop.interval / 100);
+ uint16_t initialCountdown = htole16(wd_prop.interval / 100);
+
+ if (wd_prop.expiredTimerUse != WatchdogService::TimerUse::Reserved)
+ {
+ timerUseExpirationFlags.set(static_cast<uint8_t>(
+ wdTimerUseToIpmiTimerUse(wd_prop.expiredTimerUse)));
+ }
+
if (wd_prop.enabled)
{
- res.timer_use |= wd_running;
- res.present_countdown = htole16(wd_prop.timeRemaining / 100);
+ presentCountdown = htole16(wd_prop.timeRemaining / 100);
}
else
{
- res.present_countdown = res.initial_countdown;
+ if (wd_prop.expiredTimerUse == WatchdogService::TimerUse::Reserved)
+ {
+ presentCountdown = initialCountdown;
+ }
+ else
+ {
+ presentCountdown = 0;
+ // Automatically clear it whenever a timer expiration occurs.
+ timerNotLogFlags = false;
+ }
}
- res.timer_use |=
- static_cast<uint8_t>(wdTimerUseToIpmiTimerUse(wd_prop.timerUse));
-
// TODO: Do something about having pretimeout support
- res.pretimeout = 0;
- res.expire_flags = 0;
- memcpy(response, &res, sizeof(res));
- *data_len = sizeof(res);
+ pretimeout = 0;
+
lastCallSuccessful = true;
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(
+ static_cast<uint3_t>(wdTimerUseToIpmiTimerUse(wd_prop.timerUse)), 0,
+ wd_prop.enabled, timerNotLogFlags,
+ static_cast<uint3_t>(wdActionToIpmiAction(wd_prop.expireAction)), 0,
+ timerPreTimeoutInterrupt, 0, pretimeout, timerUseExpirationFlags,
+ initialCountdown, presentCountdown);
}
catch (const InternalFailure& e)
{
reportError();
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
catch (const std::exception& e)
{
const std::string e_str = std::string("wd_get: ") + e.what();
log<level::ERR>(e_str.c_str());
- reportError();
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
catch (...)
{
log<level::ERR>("wd_get: Unknown Error");
- reportError();
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
}
diff --git a/app/watchdog.hpp b/app/watchdog.hpp
index 3f91f4f..fa53ac7 100644
--- a/app/watchdog.hpp
+++ b/app/watchdog.hpp
@@ -6,35 +6,40 @@
*/
ipmi::RspType<> ipmiAppResetWatchdogTimer();
-/** @brief The SET watchdog IPMI command.
+/**@brief The setWatchdogTimer ipmi command.
*
- * @param[in] netfn
- * @param[in] cmd
- * @param[in] request
- * @param[in,out] response
- * @param[out] data_len
- * @param[in] context
+ * @param
+ * - timerUse
+ * - dontStopTimer
+ * - dontLog
+ * - timerAction
+ * - pretimeout
+ * - expireFlags
+ * - initialCountdown
*
- * @return IPMI_CC_OK on success, an IPMI error code otherwise.
- */
-ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context);
+ * @return completion code on success.
+ **/
+ipmi::RspType<> ipmiSetWatchdogTimer(
+ uint3_t timerUse, uint3_t reserved, bool dontStopTimer, bool dontLog,
+ uint3_t timeoutAction, uint1_t reserved1, uint3_t preTimeoutInterrupt,
+ uint1_t reserved2, uint8_t preTimeoutInterval, std::bitset<8> expFlagValue,
+ uint16_t initialCountdown);
-/** @brief The GET watchdog IPMI command.
- * @param[in] netfn
- * @param[in] cmd
- * @param[in] request
- * @param[in,out] response
- * @param[out] data_len
- * @param[in] context
+/**@brief The getWatchdogTimer ipmi command.
*
- * @return IPMI_CC_OK on success, an IPMI error code otherwise.
- */
-ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context);
+ * @return
+ * - timerUse
+ * - timerActions
+ * - pretimeout
+ * - timeruseFlags
+ * - initialCountdown
+ * - presentCountdown
+ **/
+ipmi::RspType<uint3_t, uint3_t, bool, bool, // timerUse
+ uint3_t, uint1_t, uint3_t, uint1_t, // timerAction
+ uint8_t, // pretimeout
+ std::bitset<8>, // expireFlags
+ uint16_t, // initial Countdown - Little Endian (deciseconds)
+ uint16_t // present Countdown - Little Endian (deciseconds)
+ >
+ ipmiGetWatchdogTimer();
diff --git a/app/watchdog_service.cpp b/app/watchdog_service.cpp
index 284964d..3534e89 100644
--- a/app/watchdog_service.cpp
+++ b/app/watchdog_service.cpp
@@ -16,8 +16,6 @@ using phosphor::logging::elog;
using phosphor::logging::entry;
using phosphor::logging::level;
using phosphor::logging::log;
-using sdbusplus::message::variant_ns::get;
-using sdbusplus::message::variant_ns::variant;
using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
using sdbusplus::xyz::openbmc_project::State::server::convertForMessage;
using sdbusplus::xyz::openbmc_project::State::server::Watchdog;
@@ -72,18 +70,22 @@ WatchdogService::Properties WatchdogService::getProperties()
}
try
{
- std::map<std::string, variant<bool, uint64_t, std::string>> properties;
+ std::map<std::string, std::variant<bool, uint64_t, std::string>>
+ properties;
response.read(properties);
Properties wd_prop;
- wd_prop.initialized = get<bool>(properties.at("Initialized"));
- wd_prop.enabled = get<bool>(properties.at("Enabled"));
+ wd_prop.initialized = std::get<bool>(properties.at("Initialized"));
+ wd_prop.enabled = std::get<bool>(properties.at("Enabled"));
wd_prop.expireAction = Watchdog::convertActionFromString(
- get<std::string>(properties.at("ExpireAction")));
+ std::get<std::string>(properties.at("ExpireAction")));
wd_prop.timerUse = Watchdog::convertTimerUseFromString(
- get<std::string>(properties.at("CurrentTimerUse")));
+ std::get<std::string>(properties.at("CurrentTimerUse")));
+ wd_prop.expiredTimerUse = Watchdog::convertTimerUseFromString(
+ std::get<std::string>(properties.at("ExpiredTimerUse")));
- wd_prop.interval = get<uint64_t>(properties.at("Interval"));
- wd_prop.timeRemaining = get<uint64_t>(properties.at("TimeRemaining"));
+ wd_prop.interval = std::get<uint64_t>(properties.at("Interval"));
+ wd_prop.timeRemaining =
+ std::get<uint64_t>(properties.at("TimeRemaining"));
return wd_prop;
}
catch (const std::exception& e)
@@ -121,9 +123,9 @@ T WatchdogService::getProperty(const std::string& key)
}
try
{
- variant<T> value;
+ std::variant<T> value;
response.read(value);
- return get<T>(value);
+ return std::get<T>(value);
}
catch (const std::exception& e)
{
@@ -145,7 +147,7 @@ void WatchdogService::setProperty(const std::string& key, const T& val)
{
bool wasValid = wd_service.isValid(bus);
auto request = wd_service.newMethodCall(bus, prop_intf, "Set");
- request.append(wd_intf, key, variant<T>(val));
+ request.append(wd_intf, key, std::variant<T>(val));
auto response = bus.call(request);
if (response.is_method_error())
{
@@ -153,7 +155,8 @@ void WatchdogService::setProperty(const std::string& key, const T& val)
if (wasValid)
{
// Retry the request once in case the cached service was stale
- return setProperty(key, val);
+ setProperty(key, val);
+ return;
}
log<level::ERR>("WatchdogService: Method error setting property",
entry("PROPERTY=%s", key.c_str()));
@@ -186,12 +189,12 @@ void WatchdogService::setTimerUse(TimerUse timerUse)
setProperty("CurrentTimerUse", convertForMessage(timerUse));
}
-void WatchdogService::setInterval(uint64_t interval)
+void WatchdogService::setExpiredTimerUse(TimerUse timerUse)
{
- setProperty("Interval", interval);
+ setProperty("ExpiredTimerUse", convertForMessage(timerUse));
}
-void WatchdogService::setTimeRemaining(uint64_t timeRemaining)
+void WatchdogService::setInterval(uint64_t interval)
{
- setProperty("TimeRemaining", timeRemaining);
+ setProperty("Interval", interval);
}
diff --git a/app/watchdog_service.hpp b/app/watchdog_service.hpp
index 0cf1c74..141bdb7 100644
--- a/app/watchdog_service.hpp
+++ b/app/watchdog_service.hpp
@@ -35,6 +35,7 @@ class WatchdogService
bool enabled;
Action expireAction;
TimerUse timerUse;
+ TimerUse expiredTimerUse;
uint64_t interval;
uint64_t timeRemaining;
};
@@ -78,18 +79,18 @@ class WatchdogService
*/
void setTimerUse(TimerUse timerUse);
- /** @brief Sets the value of the interval property on the host watchdog
+ /** @brief Sets the value of the ExpiredTimerUse property on the host
+ * watchdog
*
- * @param[in] interval - The new interval value
+ * @param[in] timerUse - The new timerUse value
*/
- void setInterval(uint64_t interval);
+ void setExpiredTimerUse(TimerUse timerUse);
- /** @brief Sets the value of the timeRemaining property on the host
- * watchdog
+ /** @brief Sets the value of the interval property on the host watchdog
*
- * @param[in] timeRemaining - The new timeRemaining value
+ * @param[in] interval - The new interval value
*/
- void setTimeRemaining(uint64_t timeRemaining);
+ void setInterval(uint64_t interval);
private:
/** @brief sdbusplus handle */
diff --git a/apphandler.cpp b/apphandler.cpp
index 8be092c..2430e8e 100644
--- a/apphandler.cpp
+++ b/apphandler.cpp
@@ -1,6 +1,12 @@
#include <arpa/inet.h>
+#include <fcntl.h>
#include <limits.h>
+#include <linux/i2c-dev.h>
+#include <linux/i2c.h>
#include <mapper.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <systemd/sd-bus.h>
#include <unistd.h>
@@ -14,6 +20,8 @@
#include <filesystem>
#include <fstream>
#include <ipmid/api.hpp>
+#include <ipmid/sessiondef.hpp>
+#include <ipmid/sessionhelper.hpp>
#include <ipmid/types.hpp>
#include <ipmid/utils.hpp>
#include <memory>
@@ -23,7 +31,6 @@
#include <sdbusplus/message/types.hpp>
#include <string>
#include <sys_info_param.hpp>
-#include <transporthandler.hpp>
#include <tuple>
#include <vector>
#include <xyz/openbmc_project/Common/error.hpp>
@@ -36,10 +43,6 @@ extern sd_bus* bus;
constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
constexpr auto bmc_state_property = "CurrentBMCState";
-constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
-constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
-constexpr auto bmc_guid_property = "UUID";
-constexpr auto bmc_guid_len = 16;
static constexpr auto redundancyIntf =
"xyz.openbmc_project.Software.RedundancyPriority";
@@ -57,7 +60,35 @@ using Activation =
sdbusplus::xyz::openbmc_project::Software::server::Activation;
using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
namespace fs = std::filesystem;
-namespace variant_ns = sdbusplus::message::variant_ns;
+
+#ifdef ENABLE_I2C_WHITELIST_CHECK
+typedef struct
+{
+ uint8_t busId;
+ uint8_t slaveAddr;
+ uint8_t slaveAddrMask;
+ std::vector<uint8_t> data;
+ std::vector<uint8_t> dataMask;
+} i2cMasterWRWhitelist;
+
+static std::vector<i2cMasterWRWhitelist>& getWRWhitelist()
+{
+ static std::vector<i2cMasterWRWhitelist> wrWhitelist;
+ return wrWhitelist;
+}
+
+static constexpr const char* i2cMasterWRWhitelistFile =
+ "/usr/share/ipmi-providers/master_write_read_white_list.json";
+
+static constexpr const char* filtersStr = "filters";
+static constexpr const char* busIdStr = "busId";
+static constexpr const char* slaveAddrStr = "slaveAddr";
+static constexpr const char* slaveAddrMaskStr = "slaveAddrMask";
+static constexpr const char* cmdStr = "command";
+static constexpr const char* cmdMaskStr = "commandMask";
+static constexpr int base_16 = 16;
+#endif // ENABLE_I2C_WHITELIST_CHECK
+static constexpr uint8_t maxIPMIWriteReadSize = 144;
/**
* @brief Returns the Version info from primary s/w object
@@ -70,16 +101,14 @@ namespace variant_ns = sdbusplus::message::variant_ns;
* @return On success returns the Version info from primary s/w object.
*
*/
-std::string getActiveSoftwareVersionInfo()
+std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)
{
- auto busp = getSdBus();
-
std::string revision{};
ipmi::ObjectTree objectTree;
try
{
objectTree =
- ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
+ ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf);
}
catch (sdbusplus::exception::SdBusError& e)
{
@@ -93,9 +122,9 @@ std::string getActiveSoftwareVersionInfo()
for (auto& softObject : objectTree)
{
auto service =
- ipmi::getService(*busp, redundancyIntf, softObject.first);
+ ipmi::getService(*ctx->bus, redundancyIntf, softObject.first);
auto objValueTree =
- ipmi::getManagedObjects(*busp, service, softwareRoot);
+ ipmi::getManagedObjects(*ctx->bus, service, softwareRoot);
auto minPriority = 0xFF;
for (const auto& objIter : objValueTree)
@@ -106,14 +135,14 @@ std::string getActiveSoftwareVersionInfo()
auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
auto& versionProps = intfMap.at(versionIntf);
auto& activationProps = intfMap.at(activationIntf);
- auto priority = variant_ns::get<uint8_t>(
- redundancyPriorityProps.at("Priority"));
+ auto priority =
+ std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
auto purpose =
- variant_ns::get<std::string>(versionProps.at("Purpose"));
- auto activation = variant_ns::get<std::string>(
- activationProps.at("Activation"));
+ std::get<std::string>(versionProps.at("Purpose"));
+ auto activation =
+ std::get<std::string>(activationProps.at("Activation"));
auto version =
- variant_ns::get<std::string>(versionProps.at("Version"));
+ std::get<std::string>(versionProps.at("Version"));
if ((Version::convertVersionPurposeFromString(purpose) ==
Version::VersionPurpose::BMC) &&
(Activation::convertActivationsFromString(activation) ==
@@ -154,9 +183,23 @@ bool getCurrentBmcState()
ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
bmc_state_interface, bmc_state_property);
- return variant_ns::holds_alternative<std::string>(variant) &&
- BMC::convertBMCStateFromString(
- variant_ns::get<std::string>(variant)) == BMC::BMCState::Ready;
+ return std::holds_alternative<std::string>(variant) &&
+ BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
+ BMC::BMCState::Ready;
+}
+
+bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
+{
+ try
+ {
+ return getCurrentBmcState();
+ }
+ catch (...)
+ {
+ // Nothing provided the BMC interface, therefore return whatever was
+ // configured as the default.
+ return fallbackAvailability;
+ }
}
namespace acpi_state
@@ -198,12 +241,6 @@ enum class PowerState : uint8_t
static constexpr uint8_t stateChanged = 0x80;
-struct ACPIState
-{
- uint8_t sysACPIState;
- uint8_t devACPIState;
-} __attribute__((packed));
-
std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
{ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
{ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
@@ -259,41 +296,32 @@ bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
}
} // namespace acpi_state
-ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief implements Set ACPI Power State command
+ * @param sysAcpiState - ACPI system power state to set
+ * @param devAcpiState - ACPI device power state to set
+ *
+ * @return IPMI completion code on success
+ **/
+ipmi::RspType<> ipmiSetAcpiPowerState(uint8_t sysAcpiState,
+ uint8_t devAcpiState)
{
auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
- ipmi_ret_t rc = IPMI_CC_OK;
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
- auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
-
- if (*data_len != sizeof(acpi_state::ACPIState))
- {
- log<level::ERR>("set_acpi invalid len");
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- *data_len = 0;
-
- if (req->sysACPIState & acpi_state::stateChanged)
+ if (sysAcpiState & acpi_state::stateChanged)
{
// set system power state
- s = req->sysACPIState & ~acpi_state::stateChanged;
+ s = sysAcpiState & ~acpi_state::stateChanged;
if (!acpi_state::isValidACPIState(
acpi_state::PowerStateType::sysPowerState, s))
{
log<level::ERR>("set_acpi_power sys invalid input",
entry("S=%x", s));
- return IPMI_CC_PARM_OUT_OF_RANGE;
+ return ipmi::responseParmOutOfRange();
}
// valid input
@@ -324,7 +352,7 @@ ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
{
log<level::ERR>("Failed in set ACPI system property",
entry("EXCEPTION=%s", e.what()));
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
}
}
@@ -333,16 +361,16 @@ ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
log<level::DEBUG>("Do not change system power state");
}
- if (req->devACPIState & acpi_state::stateChanged)
+ if (devAcpiState & acpi_state::stateChanged)
{
// set device power state
- s = req->devACPIState & ~acpi_state::stateChanged;
+ s = devAcpiState & ~acpi_state::stateChanged;
if (!acpi_state::isValidACPIState(
acpi_state::PowerStateType::devPowerState, s))
{
log<level::ERR>("set_acpi_power dev invalid input",
entry("S=%x", s));
- return IPMI_CC_PARM_OUT_OF_RANGE;
+ return ipmi::responseParmOutOfRange();
}
// valid input
@@ -373,7 +401,7 @@ ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
{
log<level::ERR>("Failed in set ACPI device property",
entry("EXCEPTION=%s", e.what()));
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
}
}
@@ -381,24 +409,26 @@ ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
{
log<level::DEBUG>("Do not change device power state");
}
-
- return rc;
+ return ipmi::responseSuccess();
}
-ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/**
+ * @brief implements the get ACPI power state command
+ *
+ * @return IPMI completion code plus response data on success.
+ * - ACPI system power state
+ * - ACPI device power state
+ **/
+ipmi::RspType<uint8_t, // acpiSystemPowerState
+ uint8_t // acpiDevicePowerState
+ >
+ ipmiGetAcpiPowerState()
{
- ipmi_ret_t rc = IPMI_CC_OK;
-
- auto* res = reinterpret_cast<acpi_state::ACPIState*>(response);
+ uint8_t sysAcpiState;
+ uint8_t devAcpiState;
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
- *data_len = 0;
-
try
{
auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
@@ -407,26 +437,22 @@ ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
acpi_state::sysACPIProp);
auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
- variant_ns::get<std::string>(sysACPIVal));
- res->sysACPIState =
- static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
+ std::get<std::string>(sysACPIVal));
+ sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
auto devACPIVal = ipmi::getDbusProperty(
bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
acpi_state::devACPIProp);
auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
- variant_ns::get<std::string>(devACPIVal));
- res->devACPIState =
- static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
-
- *data_len = sizeof(acpi_state::ACPIState);
+ std::get<std::string>(devACPIVal));
+ devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
}
catch (const InternalFailure& e)
{
- log<level::ERR>("Failed in get ACPI property");
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
- return rc;
+
+ return ipmi::responseSuccess(sysAcpiState, devAcpiState);
}
typedef struct
@@ -524,16 +550,31 @@ int convertVersion(std::string s, Revision& rev)
return 0;
}
-auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
- uint8_t, // Device Revision
- uint8_t, // Firmware Revision Major
- uint8_t, // Firmware Revision minor
- uint8_t, // IPMI version
- uint8_t, // Additional device support
- uint24_t, // MFG ID
- uint16_t, // Product ID
- uint32_t // AUX info
- >
+/* @brief: Implement the Get Device ID IPMI command per the IPMI spec
+ * @param[in] ctx - shared_ptr to an IPMI context struct
+ *
+ * @returns IPMI completion code plus response data
+ * - Device ID (manufacturer defined)
+ * - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit]
+ * - FW revision major[7 bits] (binary encoded); available[1 bit]
+ * - FW Revision minor (BCD encoded)
+ * - IPMI version (0x02 for IPMI 2.0)
+ * - device support (bitfield of supported options)
+ * - MFG IANA ID (3 bytes)
+ * - product ID (2 bytes)
+ * - AUX info (4 bytes)
+ */
+ipmi::RspType<uint8_t, // Device ID
+ uint8_t, // Device Revision
+ uint8_t, // Firmware Revision Major
+ uint8_t, // Firmware Revision minor
+ uint8_t, // IPMI version
+ uint8_t, // Additional device support
+ uint24_t, // MFG ID
+ uint16_t, // Product ID
+ uint32_t // AUX info
+ >
+ ipmiAppGetDeviceId(ipmi::Context::ptr ctx)
{
int r = -1;
Revision rev = {0};
@@ -549,6 +590,7 @@ auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
uint32_t aux;
} devId;
static bool dev_id_initialized = false;
+ static bool defaultActivationSetting = true;
const char* filename = "/usr/share/ipmi-providers/dev_id.json";
constexpr auto ipmiDevIdStateShift = 7;
constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
@@ -557,7 +599,7 @@ auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
{
try
{
- auto version = getActiveSoftwareVersionInfo();
+ auto version = getActiveSoftwareVersionInfo(ctx);
r = convertVersion(version, rev);
}
catch (const std::exception& e)
@@ -596,6 +638,9 @@ auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
devId.prodId = data.value("prod_id", 0);
devId.aux = data.value("aux", 0);
+ // Set the availablitity of the BMC.
+ defaultActivationSetting = data.value("availability", true);
+
// Don't read the file every time if successful
dev_id_initialized = true;
}
@@ -614,7 +659,7 @@ auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
// Set availability to the actual current BMC state
devId.fw[0] &= ipmiDevIdFw1Mask;
- if (!getCurrentBmcState())
+ if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
{
devId.fw[0] |= (1 << ipmiDevIdStateShift);
}
@@ -651,120 +696,75 @@ auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
return ipmi::responseSuccess(notImplemented, zero);
}
-ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+static constexpr size_t uuidBinaryLength = 16;
+static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
{
- const char* objname = "/org/openbmc/control/chassis0";
- const char* iface = "org.freedesktop.DBus.Properties";
- const char* chassis_iface = "org.openbmc.control.Chassis";
- sd_bus_message* reply = NULL;
- sd_bus_error error = SD_BUS_ERROR_NULL;
- int r = 0;
- char* uuid = NULL;
- char* busname = NULL;
-
+ using Argument = xyz::openbmc_project::Common::InvalidArgument;
// UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
// Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
// order
// Ex: 0x2332fc2c40e66298e511f2782395a361
-
- const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
- uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
- // Point resp end of array to save in reverse order
- int resp_loc = resp_size - 1;
- int i = 0;
- char* tokptr = NULL;
- char* id_octet = NULL;
- size_t total_uuid_size = 0;
- // 1 byte of resp is built from 2 chars of uuid.
- constexpr size_t max_uuid_size = 2 * resp_size;
-
- // Status code.
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = 0;
-
- // Call Get properties method with the interface and property name
- r = mapper_get_service(bus, objname, &busname);
- if (r < 0)
- {
- log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
- entry("ERRNO=0x%X", -r));
- goto finish;
- }
- r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
- "ss", chassis_iface, "uuid");
- if (r < 0)
- {
- log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- goto finish;
- }
-
- r = sd_bus_message_read(reply, "v", "s", &uuid);
- if (r < 0 || uuid == NULL)
+ constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
+ constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
+ std::array<uint8_t, uuidBinaryLength> uuid;
+ if (rfc4122.size() == uuidRfc4122Length)
{
- log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
- rc = IPMI_CC_RESPONSE_ERROR;
- goto finish;
+ rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
+ rfc4122.end());
}
-
- // Traverse the UUID
- // Get the UUID octects separated by dash
- id_octet = strtok_r(uuid, "-", &tokptr);
-
- if (id_octet == NULL)
+ if (rfc4122.size() != uuidHexLength)
{
- // Error
- log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
- rc = IPMI_CC_RESPONSE_ERROR;
- goto finish;
+ elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
+ Argument::ARGUMENT_VALUE(rfc4122.c_str()));
}
-
- while (id_octet != NULL)
+ for (size_t ind = 0; ind < uuidHexLength; ind += 2)
{
- // Calculate the octet string size since it varies
- // Divide it by 2 for the array size since 1 byte is built from 2 chars
- int tmp_size = strlen(id_octet) / 2;
-
- // Check if total UUID size has been exceeded
- if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
+ char v[3];
+ v[0] = rfc4122[ind];
+ v[1] = rfc4122[ind + 1];
+ v[2] = 0;
+ size_t err;
+ long b;
+ try
{
- // Error - UUID too long to store
- log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
- rc = IPMI_CC_RESPONSE_ERROR;
- goto finish;
+ b = std::stoul(v, &err, 16);
}
-
- for (i = 0; i < tmp_size; i++)
+ catch (std::exception& e)
+ {
+ elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
+ Argument::ARGUMENT_VALUE(rfc4122.c_str()));
+ }
+ // check that exactly two ascii bytes were converted
+ if (err != 2)
{
- // Holder of the 2 chars that will become a byte
- char tmp_array[3] = {0};
- strncpy(tmp_array, id_octet, 2); // 2 chars at a time
-
- int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
- // Copy end to first
- std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
- resp_loc--;
- id_octet += 2; // Finished with the 2 chars, advance
+ elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
+ Argument::ARGUMENT_VALUE(rfc4122.c_str()));
}
- id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
+ uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
}
+ return uuid;
+}
+
+auto ipmiAppGetDeviceGuid()
+ -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
+{
+ // return a fixed GUID based on /etc/machine-id
+ // This should match the /redfish/v1/Managers/bmc's UUID data
- // Data length
- *data_len = resp_size;
+ // machine specific application ID (for BMC ID)
+ // generated by systemd-id128 -p new as per man page
+ static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
+ e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
- // Pack the actual response
- std::memcpy(response, &resp_uuid, *data_len);
+ sd_id128_t bmcUuid;
+ // create the UUID from /etc/machine-id via the systemd API
+ sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
-finish:
- sd_bus_error_free(&error);
- reply = sd_bus_message_unref(reply);
- free(busname);
+ char bmcUuidCstr[SD_ID128_STRING_MAX];
+ std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
- return rc;
+ std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
+ return ipmi::responseSuccess(uuid);
}
auto ipmiAppGetBtCapabilities()
@@ -782,210 +782,524 @@ auto ipmiAppGetBtCapabilities()
outputBufferSize, transactionTime, nrRetries);
}
-ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
-
+auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>>
{
- ipmi_ret_t rc = IPMI_CC_OK;
- sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+ static constexpr auto bmcInterface =
+ "xyz.openbmc_project.Inventory.Item.Bmc";
+ static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID";
+ static constexpr auto uuidProperty = "UUID";
+ ipmi::Value propValue;
try
{
// Get the Inventory object implementing BMC interface
- ipmi::DbusObjectInfo bmcObject =
- ipmi::getDbusObject(bus, bmc_interface);
+ auto busPtr = getSdBus();
+ auto objectInfo = ipmi::getDbusObject(*busPtr, bmcInterface);
// Read UUID property value from bmcObject
// UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
- auto variant =
- ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
- bmc_guid_interface, bmc_guid_property);
- std::string guidProp = variant_ns::get<std::string>(variant);
-
- // Erase "-" characters from the property value
- guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
- guidProp.end());
+ propValue =
+ ipmi::getDbusProperty(*busPtr, objectInfo.second, objectInfo.first,
+ uuidInterface, uuidProperty);
+ }
+ catch (const InternalFailure& e)
+ {
+ log<level::ERR>("Failed in reading BMC UUID property",
+ entry("INTERFACE=%s", uuidInterface),
+ entry("PROPERTY=%s", uuidProperty));
+ return ipmi::responseUnspecifiedError();
+ }
+ std::array<uint8_t, 16> uuid;
+ std::string rfc4122Uuid = std::get<std::string>(propValue);
+ try
+ {
+ // convert to IPMI format
+ uuid = rfc4122ToIpmi(rfc4122Uuid);
+ }
+ catch (const InvalidArgument& e)
+ {
+ log<level::ERR>("Failed in parsing BMC UUID property",
+ entry("INTERFACE=%s", uuidInterface),
+ entry("PROPERTY=%s", uuidProperty),
+ entry("VALUE=%s", rfc4122Uuid.c_str()));
+ return ipmi::responseUnspecifiedError();
+ }
+ return ipmi::responseSuccess(uuid);
+}
- auto guidPropLen = guidProp.length();
- // Validate UUID data
- // Divide by 2 as 1 byte is built from 2 chars
- if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
+/**
+ * @brief set the session state as teardown
+ *
+ * This function is to set the session state to tear down in progress if the
+ * state is active.
+ *
+ * @param[in] busp - Dbus obj
+ * @param[in] service - service name
+ * @param[in] obj - object path
+ *
+ * @return success completion code if it sets the session state to
+ * tearDownInProgress else return the corresponding error completion code.
+ **/
+uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
+ const std::string& service, const std::string& obj)
+{
+ try
+ {
+ uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
+ *busp, service, obj, session::sessionIntf, "State"));
+ if (sessionState == static_cast<uint8_t>(session::State::active))
{
- log<level::ERR>("Invalid UUID property value",
- entry("UUID_LENGTH=%d", guidPropLen));
- return IPMI_CC_RESPONSE_ERROR;
+ ipmi::setDbusProperty(
+ *busp, service, obj, session::sessionIntf, "State",
+ static_cast<uint8_t>(session::State::tearDownInProgress));
+ return ipmi::ccSuccess;
}
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Failed in getting session state property",
+ entry("service=%s", service.c_str()),
+ entry("object path=%s", obj.c_str()),
+ entry("interface=%s", session::sessionIntf));
+ return ipmi::ccUnspecifiedError;
+ }
- // Convert data in RFC4122(MSB) format to LSB format
- // Get 2 characters at a time as 1 byte is built from 2 chars and
- // convert to hex byte
- // TODO: Data printed for GUID command is not as per the
- // GUID format defined in IPMI specification 2.0 section 20.8
- // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
- uint8_t respGuid[bmc_guid_len];
- for (size_t i = 0, respLoc = (bmc_guid_len - 1);
- i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
- {
- auto value = static_cast<uint8_t>(
- std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
- respGuid[respLoc] = value;
- }
+ return ipmi::ccInvalidFieldRequest;
+}
- *data_len = bmc_guid_len;
- std::memcpy(response, &respGuid, bmc_guid_len);
+ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId,
+ std::optional<uint8_t> requestSessionHandle)
+{
+ auto busp = getSdBus();
+ uint8_t reqSessionHandle =
+ requestSessionHandle.value_or(session::defaultSessionHandle);
+
+ if (reqSessionId == session::sessionZero &&
+ reqSessionHandle == session::defaultSessionHandle)
+ {
+ return ipmi::response(session::ccInvalidSessionId);
}
- catch (const InternalFailure& e)
+
+ if (reqSessionId == session::sessionZero &&
+ reqSessionHandle == session::invalidSessionHandle)
{
- log<level::ERR>("Failed in reading BMC UUID property",
- entry("INTERFACE=%s", bmc_interface),
- entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
- entry("PROPERTY=%s", bmc_guid_property));
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::response(session::ccInvalidSessionHandle);
}
- return rc;
-}
-static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
+ if (reqSessionId != session::sessionZero &&
+ reqSessionHandle != session::defaultSessionHandle)
+ {
+ return ipmi::response(ipmi::ccInvalidFieldRequest);
+ }
-static std::string sysInfoReadSystemName()
-{
- // Use the BMC hostname as the "System Name."
- char hostname[HOST_NAME_MAX + 1] = {};
- if (gethostname(hostname, HOST_NAME_MAX) != 0)
+ try
{
- perror("System info parameter: system name");
+ ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
+ *busp, session::sessionManagerRootPath, session::sessionIntf);
+
+ for (auto& objectTreeItr : objectTree)
+ {
+ const std::string obj = objectTreeItr.first;
+
+ if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle))
+ {
+ auto& serviceMap = objectTreeItr.second;
+
+ // Session id and session handle are unique for each session.
+ // Session id and handler are retrived from the object path and
+ // object path will be unique for each session. Checking if
+ // multiple objects exist with same object path under multiple
+ // services.
+ if (serviceMap.size() != 1)
+ {
+ return ipmi::responseUnspecifiedError();
+ }
+
+ auto itr = serviceMap.begin();
+ const std::string service = itr->first;
+ return ipmi::response(setSessionState(busp, service, obj));
+ }
+ }
}
- return hostname;
+ catch (sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Failed to fetch object from dbus",
+ entry("INTERFACE=%s", session::sessionIntf),
+ entry("ERRMSG=%s", e.what()));
+ return ipmi::responseUnspecifiedError();
+ }
+
+ return ipmi::responseInvalidFieldRequest();
}
-struct IpmiSysInfoResp
+uint8_t getTotalSessionCount()
{
- uint8_t paramRevision;
- uint8_t setSelector;
- union
+ uint8_t count = 0, ch = 1;
+
+ while (ch < ipmi::maxIpmiChannels &&
+ count < session::maxNetworkInstanceSupported)
{
- struct
+ ipmi::ChannelInfo chInfo;
+ ipmi::getChannelInfo(ch, chInfo);
+ if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
+ ipmi::EChannelMediumType::lan8032)
{
- uint8_t encoding;
- uint8_t stringLen;
- uint8_t stringData0[14];
- } __attribute__((packed));
- uint8_t stringDataN[16];
- uint8_t byteData;
- };
-} __attribute__((packed));
+ count++;
+ }
+ ch++;
+ }
+ return count * session::maxSessionCountPerChannel;
+}
/**
- * Split a string into (up to) 16-byte chunks as expected in response for get
- * system info parameter.
+ * @brief get session info request data.
*
- * @param[in] fullString: Input string to be split
- * @param[in] chunkIndex: Index of the chunk to be written out
- * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
- * chunk_index = 0 and 16-byte capacity otherwise
- * @return the number of bytes written into the output buffer, or -EINVAL for
- * invalid arguments.
- */
-static int splitStringParam(const std::string& fullString, int chunkIndex,
- uint8_t* chunk)
+ * This function validates the request data and retrive request session id,
+ * session handle.
+ *
+ * @param[in] ctx - context of current session.
+ * @param[in] sessionIndex - request session index
+ * @param[in] payload - input payload
+ * @param[in] reqSessionId - unpacked session Id will be asigned
+ * @param[in] reqSessionHandle - unpacked session handle will be asigned
+ *
+ * @return success completion code if request data is valid
+ * else return the correcponding error completion code.
+ **/
+uint8_t getSessionInfoRequestData(const ipmi::Context::ptr ctx,
+ const uint8_t sessionIndex,
+ ipmi::message::Payload& payload,
+ uint32_t& reqSessionId,
+ uint8_t& reqSessionHandle)
{
- constexpr int maxChunk = 255;
- constexpr int smallChunk = 14;
- constexpr int chunkSize = 16;
- if (chunkIndex > maxChunk || chunk == nullptr)
+ if ((sessionIndex > session::maxSessionCountPerChannel) &&
+ (sessionIndex < session::searchSessionByHandle))
{
- return -EINVAL;
+ return ipmi::ccInvalidFieldRequest;
}
+
+ switch (sessionIndex)
+ {
+ case session::searchCurrentSession:
+
+ ipmi::ChannelInfo chInfo;
+ ipmi::getChannelInfo(ctx->channel, chInfo);
+
+ if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) !=
+ ipmi::EChannelMediumType::lan8032)
+ {
+ return ipmi::ccInvalidFieldRequest;
+ }
+
+ if (!payload.fullyUnpacked())
+ {
+ return ipmi::ccReqDataLenInvalid;
+ }
+ // Check if current sessionId is 0, sessionId 0 is reserved.
+ if (ctx->sessionId == session::sessionZero)
+ {
+ return session::ccInvalidSessionId;
+ }
+ reqSessionId = ctx->sessionId;
+ break;
+
+ case session::searchSessionByHandle:
+
+ if ((payload.unpack(reqSessionHandle)) ||
+ (!payload.fullyUnpacked()))
+ {
+ return ipmi::ccReqDataLenInvalid;
+ }
+
+ if ((reqSessionHandle == session::sessionZero) ||
+ ((reqSessionHandle & session::multiIntfaceSessionHandleMask) >
+ session::maxSessionCountPerChannel))
+ {
+ return session::ccInvalidSessionHandle;
+ }
+ break;
+
+ case session::searchSessionById:
+
+ if ((payload.unpack(reqSessionId)) || (!payload.fullyUnpacked()))
+ {
+ return ipmi::ccReqDataLenInvalid;
+ }
+
+ if (reqSessionId == session::sessionZero)
+ {
+ return session::ccInvalidSessionId;
+ }
+ break;
+
+ default:
+ if (!payload.fullyUnpacked())
+ {
+ return ipmi::ccReqDataLenInvalid;
+ }
+ break;
+ }
+ return ipmi::ccSuccess;
+}
+
+uint8_t getSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
+ const std::string& service, const std::string& objPath,
+ uint8_t& sessionState)
+{
+ try
+ {
+ sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
+ *busp, service, objPath, session::sessionIntf, "State"));
+ }
+ catch (sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Failed to fetch state property ",
+ entry("SERVICE=%s", service.c_str()),
+ entry("OBJECTPATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", session::sessionIntf),
+ entry("ERRMSG=%s", e.what()));
+ return ipmi::ccUnspecifiedError;
+ }
+
+ return ipmi::ccSuccess;
+}
+
+static constexpr uint8_t macAddrLen = 6;
+struct GetSessionInfoRes
+{
+ uint8_t sessionHandle;
+ uint8_t totalSessionCount;
+ uint8_t activeSessionCount;
+ uint8_t userID;
+ uint8_t privLevel;
+ uint8_t channelNumber;
+ uint32_t remoteIpAddr;
+ std::array<uint8_t, macAddrLen> macAddr = {0};
+ uint16_t remotePort;
+};
+
+uint8_t
+ fillGetSessionInfoRes(std::shared_ptr<sdbusplus::asio::connection>& busp,
+ const std::string& service,
+ const std::string& objPath,
+ struct GetSessionInfoRes& resp, uint8_t& sessionState)
+{
try
{
- std::string output;
- if (chunkIndex == 0)
+ ipmi::PropertyMap sessionProps = ipmi::getAllDbusProperties(
+ *busp, service, objPath, session::sessionIntf);
+
+ sessionState = std::get<uint8_t>(sessionProps.at("State"));
+ if (sessionState == static_cast<uint8_t>(session::State::active))
{
- // Output must have 14 byte capacity.
- output = fullString.substr(0, smallChunk);
+ resp.sessionHandle =
+ std::get<uint8_t>(sessionProps["SessionHandle"]);
+ resp.userID = std::get<uint8_t>(sessionProps["UserID"]);
+ resp.privLevel =
+ std::get<uint8_t>(sessionProps["CurrentPrivilege"]);
+ resp.channelNumber = std::get<uint8_t>(sessionProps["ChannelNum"]);
+ resp.remoteIpAddr =
+ std::get<uint32_t>(sessionProps["RemoteIPAddr"]);
+ resp.remotePort = std::get<uint16_t>(sessionProps["RemotePort"]);
}
- else
+ }
+ catch (sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Failed to fetch state property ",
+ entry("SERVICE=%s", service.c_str()),
+ entry("OBJECTPATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", session::sessionIntf),
+ entry("ERRMSG=%s", e.what()));
+ return ipmi::ccUnspecifiedError;
+ }
+
+ return ipmi::ccSuccess;
+}
+
+ipmi::RspType<
+ uint8_t, // session handle,
+ uint8_t, // total session count
+ uint8_t, // active session count
+ std::optional<std::tuple<uint8_t, // user ID
+ uint8_t, // privilege level
+ uint8_t, // channel number
+ uint32_t, // remote ip address,
+ std::array<uint8_t, macAddrLen>, // mac address
+ uint16_t // remote port
+ >>>
+ ipmiAppGetSessionInfo(ipmi::Context::ptr ctx, uint8_t sessionIndex,
+ ipmi::message::Payload& payload)
+{
+ uint32_t reqSessionId = 0;
+ uint8_t reqSessionHandle = session::defaultSessionHandle;
+ // initializing state to 0xff as 0 represents state as inactive.
+ uint8_t state = 0xFF;
+
+ uint8_t completionCode = getSessionInfoRequestData(
+ ctx, sessionIndex, payload, reqSessionId, reqSessionHandle);
+
+ if (completionCode)
+ {
+ return ipmi::response(completionCode);
+ }
+ struct GetSessionInfoRes res = {0};
+ res.totalSessionCount = getTotalSessionCount();
+ res.activeSessionCount = 0;
+ auto busp = getSdBus();
+
+ try
+ {
+ uint8_t index = 0;
+ ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
+ *busp, session::sessionManagerRootPath, session::sessionIntf);
+
+ for (auto& objectTreeItr : objectTree)
{
- // Output must have 16 byte capacity.
- output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
+ uint32_t sessionId = 0;
+ uint8_t sessionHandle = session::defaultSessionHandle;
+ std::string objectPath = objectTreeItr.first;
+
+ if (!parseCloseSessionInputPayload(objectPath, sessionId,
+ sessionHandle))
+ {
+ continue;
+ }
+ index++;
+ auto& serviceMap = objectTreeItr.second;
+ auto itr = serviceMap.begin();
+
+ if (serviceMap.size() != 1)
+ {
+ return ipmi::responseUnspecifiedError();
+ }
+
+ std::string service = itr->first;
+ uint8_t sessionState = 0;
+ completionCode =
+ getSessionState(busp, service, objectPath, sessionState);
+ if (completionCode)
+ {
+ return ipmi::response(completionCode);
+ }
+
+ if (sessionState == static_cast<uint8_t>(session::State::active))
+ {
+ res.activeSessionCount++;
+ }
+
+ if (index != sessionIndex && reqSessionId != sessionId &&
+ reqSessionHandle != sessionHandle)
+ {
+ continue;
+ }
+
+ completionCode =
+ fillGetSessionInfoRes(busp, service, objectPath, res, state);
+
+ if (completionCode)
+ {
+ return ipmi::response(completionCode);
+ }
}
+ }
+
+ catch (sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Failed to fetch object from dbus",
+ entry("INTERFACE=%s", session::sessionIntf),
+ entry("ERRMSG=%s", e.what()));
+ return ipmi::responseUnspecifiedError();
+ }
- std::memcpy(chunk, output.c_str(), output.length());
- return output.length();
+ if (state == static_cast<uint8_t>(session::State::active))
+ {
+ return ipmi::responseSuccess(
+ res.sessionHandle, res.totalSessionCount, res.activeSessionCount,
+ std::make_tuple(res.userID, res.privLevel, res.channelNumber,
+ res.remoteIpAddr, res.macAddr, res.remotePort));
}
- catch (const std::out_of_range& e)
+ else if (state == static_cast<uint8_t>(session::State::tearDownInProgress))
{
- // The position was beyond the end.
- return -EINVAL;
+ res.sessionHandle = 0;
+ return ipmi::responseSuccess(res.sessionHandle, res.totalSessionCount,
+ res.activeSessionCount, std::nullopt);
}
+
+ return ipmi::responseInvalidFieldRequest();
}
-/**
- * Packs the Get Sys Info Request Item into the response.
- *
- * @param[in] paramString - the parameter.
- * @param[in] setSelector - the selector
- * @param[in,out] resp - the System info response.
- * @return The number of bytes packed or failure from splitStringParam().
- */
-static int packGetSysInfoResp(const std::string& paramString,
- uint8_t setSelector, IpmiSysInfoResp* resp)
+static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
+
+static std::string sysInfoReadSystemName()
{
- uint8_t* dataBuffer = resp->stringDataN;
- resp->setSelector = setSelector;
- if (resp->setSelector == 0) // First chunk has only 14 bytes.
+ // Use the BMC hostname as the "System Name."
+ char hostname[HOST_NAME_MAX + 1] = {};
+ if (gethostname(hostname, HOST_NAME_MAX) != 0)
{
- resp->encoding = 0;
- resp->stringLen = paramString.length();
- dataBuffer = resp->stringData0;
+ perror("System info parameter: system name");
}
- return splitStringParam(paramString, resp->setSelector, dataBuffer);
+ return hostname;
}
-ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t dataLen,
- ipmi_context_t context)
+static constexpr uint8_t revisionOnly = 0x80;
+static constexpr uint8_t paramRevision = 0x11;
+static constexpr size_t configParameterLength = 16;
+
+static constexpr size_t smallChunkSize = 14;
+static constexpr size_t fullChunkSize = 16;
+static constexpr uint8_t progressMask = 0x3;
+
+static constexpr uint8_t setComplete = 0x0;
+static constexpr uint8_t setInProgress = 0x1;
+static constexpr uint8_t commitWrite = 0x2;
+static uint8_t transferStatus = setComplete;
+
+static constexpr uint8_t configDataOverhead = 2;
+
+// For EFI based system, 256 bytes is recommended.
+static constexpr size_t maxBytesPerParameter = 256;
+
+namespace ipmi
{
- IpmiSysInfoResp resp = {};
- size_t respLen = 0;
- uint8_t* const reqData = static_cast<uint8_t*>(request);
- std::string paramString;
- bool found;
- std::tuple<bool, std::string> ret;
- constexpr int minRequestSize = 4;
- constexpr int paramSelector = 1;
- constexpr uint8_t revisionOnly = 0x80;
- const uint8_t paramRequested = reqData[paramSelector];
- int rc;
+constexpr Cc ccParmNotSupported = 0x80;
+constexpr Cc ccSetInProgressActive = 0x81;
+constexpr Cc ccSystemInfoParameterSetReadOnly = 0x82;
- if (*dataLen < minRequestSize)
+static inline auto responseParmNotSupported()
+{
+ return response(ccParmNotSupported);
+}
+static inline auto responseSetInProgressActive()
+{
+ return response(ccSetInProgressActive);
+}
+static inline auto responseSystemInfoParameterSetReadOnly()
+{
+ return response(ccSystemInfoParameterSetReadOnly);
+}
+} // namespace ipmi
+
+ipmi::RspType<uint8_t, // Parameter revision
+ std::optional<uint8_t>, // data1 / setSelector / ProgressStatus
+ std::optional<std::vector<uint8_t>>> // data2-17
+ ipmiAppGetSystemInfo(uint8_t getRevision, uint8_t paramSelector,
+ uint8_t setSelector, uint8_t BlockSelector)
+{
+ if (getRevision & revisionOnly)
{
- return IPMI_CC_REQ_DATA_LEN_INVALID;
+ return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt);
}
- *dataLen = 0; // default to 0.
-
- // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
- resp.paramRevision = 0x11;
- if (reqData[0] & revisionOnly) // Get parameter revision only
+ if (paramSelector == 0)
{
- respLen = 1;
- goto writeResponse;
+ return ipmi::responseSuccess(paramRevision, transferStatus,
+ std::nullopt);
}
- // The "Set In Progress" parameter can be used for rollback of parameter
- // data and is not implemented.
- if (paramRequested == 0)
+ if (BlockSelector != 0) // 00h if parameter does not require a block number
{
- resp.byteData = 0;
- respLen = 2;
- goto writeResponse;
+ return ipmi::responseParmNotSupported();
}
if (sysInfoParamStore == nullptr)
@@ -996,30 +1310,323 @@ ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
}
// Parameters other than Set In Progress are assumed to be strings.
- ret = sysInfoParamStore->lookup(paramRequested);
- found = std::get<0>(ret);
- paramString = std::get<1>(ret);
+ std::tuple<bool, std::string> ret =
+ sysInfoParamStore->lookup(paramSelector);
+ bool found = std::get<0>(ret);
if (!found)
{
- return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
+ return ipmi::responseParmNotSupported();
+ }
+ std::string& paramString = std::get<1>(ret);
+ std::vector<uint8_t> configData;
+ size_t count = 0;
+ if (setSelector == 0)
+ { // First chunk has only 14 bytes.
+ configData.emplace_back(0); // encoding
+ configData.emplace_back(paramString.length()); // string length
+ count = std::min(paramString.length(), smallChunkSize);
+ configData.resize(count + configDataOverhead);
+ std::copy_n(paramString.begin(), count,
+ configData.begin() + configDataOverhead); // 14 bytes thunk
}
- // TODO: Cache each parameter across multiple calls, until the whole string
- // has been read out. Otherwise, it's possible for a parameter to change
- // between requests for its chunks, returning chunks incoherent with each
- // other. For now, the parameter store is simply required to have only
- // idempotent callbacks.
- rc = packGetSysInfoResp(paramString, reqData[2], &resp);
- if (rc == -EINVAL)
+ else
{
- return IPMI_CC_RESPONSE_ERROR;
+ size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
+ if (offset >= paramString.length())
+ {
+ return ipmi::responseParmOutOfRange();
+ }
+ count = std::min(paramString.length() - offset, fullChunkSize);
+ configData.resize(count);
+ std::copy_n(paramString.begin() + offset, count,
+ configData.begin()); // 16 bytes chunk
+ }
+ return ipmi::responseSuccess(paramRevision, setSelector, configData);
+}
+
+ipmi::RspType<> ipmiAppSetSystemInfo(uint8_t paramSelector, uint8_t data1,
+ std::vector<uint8_t> configData)
+{
+ if (paramSelector == 0)
+ {
+ // attempt to set the 'set in progress' value (in parameter #0)
+ // when not in the set complete state.
+ if ((transferStatus != setComplete) && (data1 == setInProgress))
+ {
+ return ipmi::responseSetInProgressActive();
+ }
+ // only following 2 states are supported
+ if (data1 > setInProgress)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "illegal SetInProgress status");
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ transferStatus = data1 & progressMask;
+ return ipmi::responseSuccess();
+ }
+
+ if (configData.size() > configParameterLength)
+ {
+ return ipmi::responseInvalidFieldRequest();
}
- respLen = sizeof(resp); // Write entire string data chunk in response.
+ if (!sysInfoParamStore)
+ {
+ sysInfoParamStore = std::make_unique<SysInfoParamStore>();
+ sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
+ sysInfoReadSystemName);
+ }
+
+ // lookup
+ std::tuple<bool, std::string> ret =
+ sysInfoParamStore->lookup(paramSelector);
+ bool found = std::get<0>(ret);
+ std::string& paramString = std::get<1>(ret);
+ if (!found)
+ {
+ // parameter does not exist. Init new
+ paramString = "";
+ }
-writeResponse:
- std::memcpy(response, &resp, sizeof(resp));
- *dataLen = respLen;
- return IPMI_CC_OK;
+ uint8_t setSelector = data1;
+ size_t count = 0;
+ if (setSelector == 0) // First chunk has only 14 bytes.
+ {
+ size_t stringLen = configData.at(1); // string length
+ // maxBytesPerParamter is 256. It will always be greater than stringLen
+ // (unit8_t) if maxBytes changes in future, then following line is
+ // needed.
+ // stringLen = std::min(stringLen, maxBytesPerParameter);
+ count = std::min(stringLen, smallChunkSize);
+ count = std::min(count, configData.size());
+ paramString.resize(stringLen); // reserve space
+ std::copy_n(configData.begin() + configDataOverhead, count,
+ paramString.begin());
+ }
+ else
+ {
+ size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
+ if (offset >= paramString.length())
+ {
+ return ipmi::responseParmOutOfRange();
+ }
+ count = std::min(paramString.length() - offset, configData.size());
+ std::copy_n(configData.begin(), count, paramString.begin() + offset);
+ }
+ sysInfoParamStore->update(paramSelector, paramString);
+ return ipmi::responseSuccess();
+}
+
+#ifdef ENABLE_I2C_WHITELIST_CHECK
+inline std::vector<uint8_t> convertStringToData(const std::string& command)
+{
+ std::istringstream iss(command);
+ std::string token;
+ std::vector<uint8_t> dataValue;
+ while (std::getline(iss, token, ' '))
+ {
+ dataValue.emplace_back(
+ static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
+ }
+ return dataValue;
+}
+
+static bool populateI2CMasterWRWhitelist()
+{
+ nlohmann::json data = nullptr;
+ std::ifstream jsonFile(i2cMasterWRWhitelistFile);
+
+ if (!jsonFile.good())
+ {
+ log<level::WARNING>("i2c white list file not found!",
+ entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
+ return false;
+ }
+
+ try
+ {
+ data = nlohmann::json::parse(jsonFile, nullptr, false);
+ }
+ catch (nlohmann::json::parse_error& e)
+ {
+ log<level::ERR>("Corrupted i2c white list config file",
+ entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
+ entry("MSG: %s", e.what()));
+ return false;
+ }
+
+ try
+ {
+ // Example JSON Structure format
+ // "filters": [
+ // {
+ // "Description": "Allow full read - ignore first byte write value
+ // for 0x40 to 0x4F",
+ // "busId": "0x01",
+ // "slaveAddr": "0x40",
+ // "slaveAddrMask": "0x0F",
+ // "command": "0x00",
+ // "commandMask": "0xFF"
+ // },
+ // {
+ // "Description": "Allow full read - first byte match 0x05 and
+ // ignore second byte",
+ // "busId": "0x01",
+ // "slaveAddr": "0x57",
+ // "slaveAddrMask": "0x00",
+ // "command": "0x05 0x00",
+ // "commandMask": "0x00 0xFF"
+ // },]
+
+ nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
+ std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
+ for (const auto& it : filters.items())
+ {
+ nlohmann::json filter = it.value();
+ if (filter.is_null())
+ {
+ log<level::ERR>(
+ "Corrupted I2C master write read whitelist config file",
+ entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
+ return false;
+ }
+ const std::vector<uint8_t>& writeData =
+ convertStringToData(filter[cmdStr].get<std::string>());
+ const std::vector<uint8_t>& writeDataMask =
+ convertStringToData(filter[cmdMaskStr].get<std::string>());
+ if (writeDataMask.size() != writeData.size())
+ {
+ log<level::ERR>("I2C master write read whitelist filter "
+ "mismatch for command & mask size");
+ return false;
+ }
+ whitelist.push_back(
+ {static_cast<uint8_t>(std::stoul(
+ filter[busIdStr].get<std::string>(), nullptr, base_16)),
+ static_cast<uint8_t>(
+ std::stoul(filter[slaveAddrStr].get<std::string>(),
+ nullptr, base_16)),
+ static_cast<uint8_t>(
+ std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
+ nullptr, base_16)),
+ writeData, writeDataMask});
+ }
+ if (whitelist.size() != filters.size())
+ {
+ log<level::ERR>(
+ "I2C master write read whitelist filter size mismatch");
+ return false;
+ }
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("I2C master write read whitelist unexpected exception",
+ entry("ERROR=%s", e.what()));
+ return false;
+ }
+ return true;
+}
+
+static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
+ const std::vector<uint8_t>& dataMask,
+ const std::vector<uint8_t>& writeData)
+{
+ std::vector<uint8_t> processedDataBuf(data.size());
+ std::vector<uint8_t> processedReqBuf(dataMask.size());
+ std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
+ processedReqBuf.begin(), std::bit_or<uint8_t>());
+ std::transform(data.begin(), data.end(), dataMask.begin(),
+ processedDataBuf.begin(), std::bit_or<uint8_t>());
+
+ return (processedDataBuf == processedReqBuf);
+}
+
+static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
+ std::vector<uint8_t>& writeData)
+{
+ std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
+ for (const auto& wlEntry : whiteList)
+ {
+ if ((busId == wlEntry.busId) &&
+ ((slaveAddr | wlEntry.slaveAddrMask) ==
+ (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
+ {
+ const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
+ // Skip as no-match, if requested write data is more than the
+ // write data mask size
+ if (writeData.size() > dataMask.size())
+ {
+ continue;
+ }
+ if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#else
+static bool populateI2CMasterWRWhitelist()
+{
+ log<level::INFO>(
+ "I2C_WHITELIST_CHECK is disabled, do not populate whitelist");
+ return true;
+}
+#endif // ENABLE_I2C_WHITELIST_CHECK
+
+/** @brief implements master write read IPMI command which can be used for
+ * low-level I2C/SMBus write, read or write-read access
+ * @param isPrivateBus -to indicate private bus usage
+ * @param busId - bus id
+ * @param channelNum - channel number
+ * @param reserved - skip 1 bit
+ * @param slaveAddr - slave address
+ * @param read count - number of bytes to be read
+ * @param writeData - data to be written
+ *
+ * @returns IPMI completion code plus response data
+ * - readData - i2c response data
+ */
+ipmi::RspType<std::vector<uint8_t>>
+ ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
+ bool reserved, uint7_t slaveAddr, uint8_t readCount,
+ std::vector<uint8_t> writeData)
+{
+ if (readCount > maxIPMIWriteReadSize)
+ {
+ log<level::ERR>("Master write read command: Read count exceeds limit");
+ return ipmi::responseParmOutOfRange();
+ }
+ const size_t writeCount = writeData.size();
+ if (!readCount && !writeCount)
+ {
+ log<level::ERR>("Master write read command: Read & write count are 0");
+ return ipmi::responseInvalidFieldRequest();
+ }
+#ifdef ENABLE_I2C_WHITELIST_CHECK
+ if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
+ static_cast<uint8_t>(slaveAddr), writeData))
+ {
+ log<level::ERR>("Master write read request blocked!",
+ entry("BUS=%d", static_cast<uint8_t>(busId)),
+ entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
+ return ipmi::responseInvalidFieldRequest();
+ }
+#endif // ENABLE_I2C_WHITELIST_CHECK
+ std::vector<uint8_t> readBuf(readCount);
+ std::string i2cBus =
+ "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
+
+ ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(slaveAddr),
+ writeData, readBuf);
+ if (ret != ipmi::ccSuccess)
+ {
+ return ipmi::response(ret);
+ }
+ return ipmi::responseSuccess(readBuf);
}
void register_netfn_app_functions()
@@ -1039,13 +1646,23 @@ void register_netfn_app_functions()
ipmi::app::cmdResetWatchdogTimer,
ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdGetSessionInfo, ipmi::Privilege::User,
+ ipmiAppGetSessionInfo);
+
// <Set Watchdog Timer>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
- ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdSetWatchdogTimer,
+ ipmi::Privilege::Operator, ipmiSetWatchdogTimer);
+
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdCloseSession, ipmi::Privilege::Callback,
+ ipmiAppCloseSession);
// <Get Watchdog Timer>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
- ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdGetWatchdogTimer, ipmi::Privilege::User,
+ ipmiGetWatchdogTimer);
// <Get Self Test Results>
ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
@@ -1053,27 +1670,48 @@ void register_netfn_app_functions()
ipmi::Privilege::User, ipmiAppGetSelfTestResults);
// <Get Device GUID>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
- ipmi_app_get_device_guid, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
+ ipmiAppGetDeviceGuid);
// <Set ACPI Power State>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
- ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
-
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdSetAcpiPowerState,
+ ipmi::Privilege::Admin, ipmiSetAcpiPowerState);
// <Get ACPI Power State>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL,
- ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdGetAcpiPowerState,
+ ipmi::Privilege::User, ipmiGetAcpiPowerState);
+
+ // Note: For security reason, this command will be registered only when
+ // there are proper I2C Master write read whitelist
+ if (populateI2CMasterWRWhitelist())
+ {
+ // Note: For security reasons, registering master write read as admin
+ // privilege command, even though IPMI 2.0 specification allows it as
+ // operator privilege.
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdMasterWriteRead,
+ ipmi::Privilege::Admin, ipmiMasterWriteRead);
+ }
// <Get System GUID Command>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
- ipmi_app_get_sys_guid, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
+ ipmiAppGetSystemGuid);
// <Get Channel Cipher Suites Command>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
- getChannelCipherSuites, PRIVILEGE_CALLBACK);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdGetChannelCipherSuites,
+ ipmi::Privilege::None, getChannelCipherSuites);
// <Get System Info Command>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
- ipmi_app_get_system_info, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdGetSystemInfoParameters,
+ ipmi::Privilege::User, ipmiAppGetSystemInfo);
+ // <Set System Info Command>
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdSetSystemInfoParameters,
+ ipmi::Privilege::Admin, ipmiAppSetSystemInfo);
return;
}
diff --git a/apphandler.hpp b/apphandler.hpp
index d4dd8e8..4f03121 100644
--- a/apphandler.hpp
+++ b/apphandler.hpp
@@ -19,7 +19,6 @@ enum ipmi_netfn_app_cmds
IPMI_CMD_SET_CHAN_ACCESS = 0x40,
IPMI_CMD_GET_CHANNEL_ACCESS = 0x41,
IPMI_CMD_GET_CHAN_INFO = 0x42,
- IPMI_CMD_GET_CHAN_CIPHER_SUITES = 0x54,
IPMI_CMD_SET_SYSTEM_INFO = 0x58,
IPMI_CMD_GET_SYSTEM_INFO = 0x59,
};
diff --git a/chassishandler.cpp b/chassishandler.cpp
index f8a93b0..6e1341c 100644
--- a/chassishandler.cpp
+++ b/chassishandler.cpp
@@ -2,8 +2,6 @@
#include "chassishandler.hpp"
-#include "settings.hpp"
-
#include <arpa/inet.h>
#include <endian.h>
#include <limits.h>
@@ -26,6 +24,7 @@
#include <sdbusplus/message/types.hpp>
#include <sdbusplus/server/object.hpp>
#include <sdbusplus/timer.hpp>
+#include <settings.hpp>
#include <sstream>
#include <string>
#include <xyz/openbmc_project/Common/error.hpp>
@@ -44,6 +43,8 @@
std::unique_ptr<phosphor::Timer> identifyTimer
__attribute__((init_priority(101)));
+static ChassisIDState chassisIDState = ChassisIDState::reserved;
+
constexpr size_t SIZE_MAC = 18;
constexpr size_t SIZE_BOOT_OPTION = (uint8_t)
BootOptionResponseSize::OPAL_NETWORK_SETTINGS; // Maximum size of the boot
@@ -100,33 +101,14 @@ const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress";
const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress";
static constexpr uint8_t chassisCapFlagMask = 0x0f;
static constexpr uint8_t chassisCapAddrMask = 0xfe;
-
-typedef struct
-{
- uint8_t cap_flags;
- uint8_t fru_info_dev_addr;
- uint8_t sdr_dev_addr;
- uint8_t sel_dev_addr;
- uint8_t system_management_dev_addr;
- uint8_t bridge_dev_addr;
-} __attribute__((packed)) ipmi_chassis_cap_t;
-
-typedef struct
-{
- uint8_t cur_power_state;
- uint8_t last_power_event;
- uint8_t misc_power_state;
- uint8_t front_panel_button_cap_status;
-} __attribute__((packed)) ipmi_get_chassis_status_t;
-
-/**
- * @struct Get POH counter command response data
- */
-struct GetPOHCountResponse
-{
- uint8_t minPerCount; ///< Minutes per count
- uint8_t counterReading[4]; ///< Counter reading
-} __attribute__((packed));
+static constexpr const char* powerButtonIntf =
+ "xyz.openbmc_project.Chassis.Buttons.Power";
+static constexpr const char* powerButtonPath =
+ "/xyz/openbmc_project/Chassis/Buttons/Power0";
+static constexpr const char* resetButtonIntf =
+ "xyz.openbmc_project.Chassis.Buttons.Reset";
+static constexpr const char* resetButtonPath =
+ "/xyz/openbmc_project/Chassis/Buttons/Reset0";
// Phosphor Host State manager
namespace State = sdbusplus::xyz::openbmc_project::State::server;
@@ -136,7 +118,6 @@ namespace fs = std::filesystem;
using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
-namespace variant_ns = sdbusplus::message::variant_ns;
namespace chassis
{
@@ -152,8 +133,18 @@ sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
namespace cache
{
-settings::Objects objects(dbus,
- {bootModeIntf, bootSourceIntf, powerRestoreIntf});
+std::unique_ptr<settings::Objects> objectsPtr = nullptr;
+
+settings::Objects& getObjects()
+{
+ if (objectsPtr == nullptr)
+ {
+ objectsPtr = std::make_unique<settings::Objects>(
+ dbus, std::vector<std::string>{bootModeIntf, bootSourceIntf,
+ powerRestoreIntf});
+ }
+ return *objectsPtr;
+}
} // namespace cache
} // namespace internal
@@ -216,19 +207,19 @@ int getHostNetworkData(get_sys_boot_options_response_t* respptr)
macObjectInfo.first, MAC_INTERFACE,
"MACAddress");
- auto ipAddress = variant_ns::get<std::string>(properties["Address"]);
+ auto ipAddress = std::get<std::string>(properties["Address"]);
- auto gateway = variant_ns::get<std::string>(properties["Gateway"]);
+ auto gateway = std::get<std::string>(properties["Gateway"]);
- auto prefix = variant_ns::get<uint8_t>(properties["PrefixLength"]);
+ auto prefix = std::get<uint8_t>(properties["PrefixLength"]);
uint8_t isStatic =
- (variant_ns::get<std::string>(properties["Origin"]) ==
+ (std::get<std::string>(properties["Origin"]) ==
"xyz.openbmc_project.Network.IP.AddressOrigin.Static")
? 1
: 0;
- auto MACAddress = variant_ns::get<std::string>(variant);
+ auto MACAddress = std::get<std::string>(variant);
// it is expected here that we should get the valid data
// but we may also get the default values.
@@ -266,11 +257,10 @@ int getHostNetworkData(get_sys_boot_options_response_t* respptr)
std::memcpy(respptr->data + ADDRTYPE_OFFSET, &isStatic,
sizeof(isStatic));
- uint8_t addressFamily =
- (variant_ns::get<std::string>(properties["Type"]) ==
- "xyz.openbmc_project.Network.IP.Protocol.IPv4")
- ? AF_INET
- : AF_INET6;
+ uint8_t addressFamily = (std::get<std::string>(properties["Type"]) ==
+ "xyz.openbmc_project.Network.IP.Protocol.IPv4")
+ ? AF_INET
+ : AF_INET6;
addrSize = (addressFamily == AF_INET)
? ipmi::network::IPV4_ADDRESS_SIZE_BYTE
@@ -504,39 +494,29 @@ uint32_t getPOHCounter()
ipmi::getDbusProperty(bus, service, chassisStateObj.first,
chassisPOHStateIntf, pOHCounterProperty);
- return variant_ns::get<uint32_t>(propValue);
-}
-
-ipmi_ret_t ipmi_chassis_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
-{
- // Status code.
- ipmi_ret_t rc = IPMI_CC_INVALID;
- *data_len = 0;
- return rc;
+ return std::get<uint32_t>(propValue);
}
-ipmi_ret_t ipmi_get_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief Implements the get chassis capabilities command
+ *
+ * @returns IPMI completion code plus response data
+ * chassisCapFlags - chassis capability flag
+ * chassisFRUInfoDevAddr - chassis FRU info Device Address
+ * chassisSDRDevAddr - chassis SDR device address
+ * chassisSELDevAddr - chassis SEL device address
+ * chassisSMDevAddr - chassis system management device address
+ * chassisBridgeDevAddr - chassis bridge device address
+ */
+ipmi::RspType<uint8_t, // chassis capabilities flag
+ uint8_t, // chassis FRU info Device Address
+ uint8_t, // chassis SDR device address
+ uint8_t, // chassis SEL device address
+ uint8_t, // chassis system management device address
+ uint8_t // chassis bridge device address
+ >
+ ipmiGetChassisCap()
{
- // sd_bus error
- ipmi_ret_t rc = IPMI_CC_OK;
-
- ipmi_chassis_cap_t chassis_cap{};
-
- if (*data_len != 0)
- {
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- *data_len = sizeof(ipmi_chassis_cap_t);
-
+ ipmi::PropertyMap properties;
try
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
@@ -555,120 +535,135 @@ ipmi_ret_t ipmi_get_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
// interfaces).
// [0] -1b = Chassis provides intrusion (physical security) sensor.
// set to default value 0x0.
- ipmi::Value variant = ipmi::getDbusProperty(
- bus, chassisCapObject.second, chassisCapObject.first,
- chassisCapIntf, chassisCapFlagsProp);
- chassis_cap.cap_flags = variant_ns::get<uint8_t>(variant);
-
- variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
- chassisCapObject.first, chassisCapIntf,
- chassisFRUDevAddrProp);
- // Chassis FRU info Device Address.
- chassis_cap.fru_info_dev_addr = variant_ns::get<uint8_t>(variant);
-
- variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
- chassisCapObject.first, chassisCapIntf,
- chassisSDRDevAddrProp);
- // Chassis SDR Device Address.
- chassis_cap.sdr_dev_addr = variant_ns::get<uint8_t>(variant);
-
- variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
- chassisCapObject.first, chassisCapIntf,
- chassisSELDevAddrProp);
- // Chassis SEL Device Address.
- chassis_cap.sel_dev_addr = variant_ns::get<uint8_t>(variant);
-
- variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
- chassisCapObject.first, chassisCapIntf,
- chassisSMDevAddrProp);
- // Chassis System Management Device Address.
- chassis_cap.system_management_dev_addr =
- variant_ns::get<uint8_t>(variant);
-
- variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
- chassisCapObject.first, chassisCapIntf,
- chassisBridgeDevAddrProp);
- // Chassis Bridge Device Address.
- chassis_cap.bridge_dev_addr = variant_ns::get<uint8_t>(variant);
- uint8_t* respP = reinterpret_cast<uint8_t*>(response);
- uint8_t* chassisP = reinterpret_cast<uint8_t*>(&chassis_cap);
- std::copy(chassisP, chassisP + *data_len, respP);
+
+ properties =
+ ipmi::getAllDbusProperties(bus, chassisCapObject.second,
+ chassisCapObject.first, chassisCapIntf);
}
catch (std::exception& e)
{
- log<level::ERR>(e.what());
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- *data_len = 0;
- return rc;
+ log<level::ERR>("Failed to fetch Chassis Capability properties",
+ entry("ERROR=%s", e.what()));
+ return ipmi::responseUnspecifiedError();
}
- return rc;
+ uint8_t* chassisCapFlags =
+ std::get_if<uint8_t>(&properties[chassisCapFlagsProp]);
+ if (chassisCapFlags == nullptr)
+ {
+ log<level::ERR>("Error to get chassis capability flags");
+ return ipmi::responseUnspecifiedError();
+ }
+ uint8_t* chassisFRUInfoDevAddr =
+ std::get_if<uint8_t>(&properties[chassisFRUDevAddrProp]);
+ if (chassisFRUInfoDevAddr == nullptr)
+ {
+ log<level::ERR>("Error to get chassis FRU info device address");
+ return ipmi::responseUnspecifiedError();
+ }
+ uint8_t* chassisSDRDevAddr =
+ std::get_if<uint8_t>(&properties[chassisSDRDevAddrProp]);
+ if (chassisSDRDevAddr == nullptr)
+ {
+ log<level::ERR>("Error to get chassis SDR device address");
+ return ipmi::responseUnspecifiedError();
+ }
+ uint8_t* chassisSELDevAddr =
+ std::get_if<uint8_t>(&properties[chassisSELDevAddrProp]);
+ if (chassisSELDevAddr == nullptr)
+ {
+ log<level::ERR>("Error to get chassis SEL device address");
+ return ipmi::responseUnspecifiedError();
+ }
+ uint8_t* chassisSMDevAddr =
+ std::get_if<uint8_t>(&properties[chassisSMDevAddrProp]);
+ if (chassisSMDevAddr == nullptr)
+ {
+ log<level::ERR>("Error to get chassis SM device address");
+ return ipmi::responseUnspecifiedError();
+ }
+ uint8_t* chassisBridgeDevAddr =
+ std::get_if<uint8_t>(&properties[chassisBridgeDevAddrProp]);
+ if (chassisBridgeDevAddr == nullptr)
+ {
+ log<level::ERR>("Error to get chassis bridge device address");
+ return ipmi::responseUnspecifiedError();
+ }
+
+ return ipmi::responseSuccess(*chassisCapFlags, *chassisFRUInfoDevAddr,
+ *chassisSDRDevAddr, *chassisSELDevAddr,
+ *chassisSMDevAddr, *chassisBridgeDevAddr);
}
-ipmi_ret_t ipmi_set_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
-{
- ipmi_ret_t rc = IPMI_CC_OK;
+/** @brief implements set chassis capalibities command
+ * @param intrusion - chassis intrusion
+ * @param fpLockout - frontpannel lockout
+ * @param reserved1 - skip one bit
+ * @param fruDeviceAddr - chassis FRU info Device Address
+ * @param sdrDeviceAddr - chassis SDR device address
+ * @param selDeviceAddr - chassis SEL device address
+ * @param smDeviceAddr - chassis system management device address
+ * @param bridgeDeviceAddr - chassis bridge device address
+ *
+ * @returns IPMI completion code
+ */
+ipmi::RspType<> ipmiSetChassisCap(bool intrusion, bool fpLockout,
+ uint6_t reserved1,
- if (*data_len != sizeof(ipmi_chassis_cap_t))
- {
- log<level::ERR>("Unsupported request length",
- entry("LEN=0x%x", *data_len));
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
+ uint8_t fruDeviceAddr,
- ipmi_chassis_cap_t* chassisCap = static_cast<ipmi_chassis_cap_t*>(request);
+ uint8_t sdrDeviceAddr,
- *data_len = 0;
+ uint8_t selDeviceAddr,
+
+ uint8_t smDeviceAddr,
+
+ uint8_t bridgeDeviceAddr)
+{
// check input data
- if (0 != (chassisCap->cap_flags & ~chassisCapFlagMask))
+ if (reserved1 != 0)
{
- log<level::ERR>("Unsupported request parameter(CAP Flags)",
- entry("REQ=0x%x", chassisCap->cap_flags));
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ log<level::ERR>("Unsupported request parameter");
+ return ipmi::responseInvalidFieldRequest();
}
- if (0 != (chassisCap->fru_info_dev_addr & ~chassisCapAddrMask))
+ if ((fruDeviceAddr & ~chassisCapAddrMask) != 0)
{
log<level::ERR>("Unsupported request parameter(FRU Addr)",
- entry("REQ=0x%x", chassisCap->fru_info_dev_addr));
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ entry("REQ=0x%x", fruDeviceAddr));
+ return ipmi::responseInvalidFieldRequest();
}
-
- if (0 != (chassisCap->sdr_dev_addr & ~chassisCapAddrMask))
+ if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0)
{
log<level::ERR>("Unsupported request parameter(SDR Addr)",
- entry("REQ=0x%x", chassisCap->sdr_dev_addr));
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ entry("REQ=0x%x", sdrDeviceAddr));
+ return ipmi::responseInvalidFieldRequest();
}
- if (0 != (chassisCap->sel_dev_addr & ~chassisCapAddrMask))
+ if ((selDeviceAddr & ~chassisCapAddrMask) != 0)
{
log<level::ERR>("Unsupported request parameter(SEL Addr)",
- entry("REQ=0x%x", chassisCap->sel_dev_addr));
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ entry("REQ=0x%x", selDeviceAddr));
+ return ipmi::responseInvalidFieldRequest();
}
- if (0 != (chassisCap->system_management_dev_addr & ~chassisCapAddrMask))
+ if ((smDeviceAddr & ~chassisCapAddrMask) != 0)
{
- log<level::ERR>(
- "Unsupported request parameter(SM Addr)",
- entry("REQ=0x%x", chassisCap->system_management_dev_addr));
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ log<level::ERR>("Unsupported request parameter(SM Addr)",
+ entry("REQ=0x%x", smDeviceAddr));
+ return ipmi::responseInvalidFieldRequest();
}
- if (0 != (chassisCap->bridge_dev_addr & ~chassisCapAddrMask))
+ if ((bridgeDeviceAddr & ~chassisCapAddrMask) != 0)
{
log<level::ERR>("Unsupported request parameter(Bridge Addr)",
- entry("REQ=0x%x", chassisCap->bridge_dev_addr));
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ entry("REQ=0x%x", bridgeDeviceAddr));
+ return ipmi::responseInvalidFieldRequest();
}
+ uint8_t capFlags = (static_cast<uint8_t>(intrusion)) |
+ ((static_cast<uint8_t>(fpLockout)) << 1);
try
{
sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
@@ -677,39 +672,34 @@ ipmi_ret_t ipmi_set_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi::setDbusProperty(bus, chassisCapObject.second,
chassisCapObject.first, chassisCapIntf,
- chassisCapFlagsProp, chassisCap->cap_flags);
+ chassisCapFlagsProp, capFlags);
ipmi::setDbusProperty(bus, chassisCapObject.second,
chassisCapObject.first, chassisCapIntf,
- chassisFRUDevAddrProp,
- chassisCap->fru_info_dev_addr);
+ chassisFRUDevAddrProp, fruDeviceAddr);
ipmi::setDbusProperty(bus, chassisCapObject.second,
chassisCapObject.first, chassisCapIntf,
- chassisSDRDevAddrProp, chassisCap->sdr_dev_addr);
+ chassisSDRDevAddrProp, sdrDeviceAddr);
ipmi::setDbusProperty(bus, chassisCapObject.second,
chassisCapObject.first, chassisCapIntf,
- chassisSELDevAddrProp, chassisCap->sel_dev_addr);
+ chassisSELDevAddrProp, selDeviceAddr);
ipmi::setDbusProperty(bus, chassisCapObject.second,
chassisCapObject.first, chassisCapIntf,
- chassisSMDevAddrProp,
- chassisCap->system_management_dev_addr);
+ chassisSMDevAddrProp, smDeviceAddr);
ipmi::setDbusProperty(bus, chassisCapObject.second,
chassisCapObject.first, chassisCapIntf,
- chassisBridgeDevAddrProp,
- chassisCap->bridge_dev_addr);
+ chassisBridgeDevAddrProp, bridgeDeviceAddr);
}
catch (std::exception& e)
{
log<level::ERR>(e.what());
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- return rc;
+ return ipmi::responseUnspecifiedError();
}
-
- return rc;
+ return ipmi::responseSuccess();
}
//------------------------------------------
@@ -770,6 +760,38 @@ int initiate_state_transition(State::Host::Transition transition)
return rc;
}
+//------------------------------------------
+// Set Enabled property to inform NMI source
+// handling to trigger a NMI_OUT BSOD.
+//------------------------------------------
+int setNmiProperty(const bool value)
+{
+ constexpr const char* nmiSourceObjPath =
+ "/xyz/openbmc_project/Chassis/Control/NMISource";
+ constexpr const char* nmiSourceIntf =
+ "xyz.openbmc_project.Chassis.Control.NMISource";
+ std::string bmcSourceSignal = "xyz.openbmc_project.Chassis.Control."
+ "NMISource.BMCSourceSignal.ChassisCmd";
+ std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+
+ try
+ {
+ auto service = ipmi::getService(*busp, nmiSourceIntf, nmiSourceObjPath);
+ ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf,
+ "BMCSource", bmcSourceSignal);
+ ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf,
+ "Enabled", value);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Failed to trigger NMI_OUT",
+ entry("EXCEPTION=%s", e.what()));
+ return -1;
+ }
+
+ return 0;
+}
+
namespace power_policy
{
@@ -777,175 +799,264 @@ using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
using IpmiValue = uint8_t;
using DbusValue = RestorePolicy::Policy;
-std::map<DbusValue, IpmiValue> dbusToIpmi = {
+const std::map<DbusValue, IpmiValue> dbusToIpmi = {
{RestorePolicy::Policy::AlwaysOff, 0x00},
{RestorePolicy::Policy::Restore, 0x01},
{RestorePolicy::Policy::AlwaysOn, 0x02}};
static constexpr uint8_t noChange = 0x03;
static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04;
-static constexpr uint8_t policyBitMask = 0x07;
-static constexpr uint8_t setPolicyReqLen = 1;
+
+/* helper function for Get Chassis Status Command
+ */
+std::optional<uint2_t> getPowerRestorePolicy()
+{
+ uint2_t restorePolicy = 0;
+ using namespace chassis::internal;
+
+ settings::Objects& objects = cache::getObjects();
+
+ try
+ {
+ const auto& powerRestoreSetting =
+ objects.map.at(powerRestoreIntf).front();
+ ipmi::Value result = ipmi::getDbusProperty(
+ *getSdBus(),
+ objects.service(powerRestoreSetting, powerRestoreIntf).c_str(),
+ powerRestoreSetting.c_str(), powerRestoreIntf,
+ "PowerRestorePolicy");
+ auto powerRestore = RestorePolicy::convertPolicyFromString(
+ std::get<std::string>(result));
+ restorePolicy = dbusToIpmi.at(powerRestore);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>(
+ "Failed to fetch pgood property", entry("ERROR=%s", e.what()),
+ entry("PATH=%s", objects.map.at(powerRestoreIntf).front().c_str()),
+ entry("INTERFACE=%s", powerRestoreIntf));
+ cache::objectsPtr.reset();
+ return std::nullopt;
+ }
+ return std::make_optional(restorePolicy);
+}
+
+/*
+ * getPowerStatus
+ * helper function for Get Chassis Status Command
+ * return - optional value for pgood (no value on error)
+ */
+std::optional<bool> getPowerStatus()
+{
+ bool powerGood = false;
+ std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+ try
+ {
+ constexpr const char* chassisStatePath =
+ "/xyz/openbmc_project/state/chassis0";
+ constexpr const char* chassisStateIntf =
+ "xyz.openbmc_project.State.Chassis";
+ auto service =
+ ipmi::getService(*busp, chassisStateIntf, chassisStatePath);
+
+ ipmi::Value powerState =
+ ipmi::getDbusProperty(*busp, service, chassisStatePath,
+ chassisStateIntf, "CurrentPowerState");
+ powerGood = std::get<std::string>(powerState) ==
+ "xyz.openbmc_project.State.Chassis.PowerState.On";
+ }
+ catch (const std::exception& e)
+ {
+ try
+ {
+ // FIXME: some legacy modules use the older path; try that next
+ constexpr const char* legacyPwrCtrlObj =
+ "/org/openbmc/control/power0";
+ constexpr const char* legacyPwrCtrlIntf =
+ "org.openbmc.control.Power";
+ auto service =
+ ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj);
+
+ ipmi::Value variant = ipmi::getDbusProperty(
+ *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood");
+ powerGood = static_cast<bool>(std::get<int>(variant));
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Failed to fetch pgood property",
+ entry("ERROR=%s", e.what()));
+ return std::nullopt;
+ }
+ }
+ return std::make_optional(powerGood);
+}
+
+/*
+ * getACFailStatus
+ * helper function for Get Chassis Status Command
+ * return - bool value for ACFail (false on error)
+ */
+bool getACFailStatus()
+{
+ constexpr const char* powerControlObj =
+ "/xyz/openbmc_project/Chassis/Control/Power0";
+ constexpr const char* powerControlIntf =
+ "xyz.openbmc_project.Chassis.Control.Power";
+ bool acFail = false;
+ std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
+ try
+ {
+ auto service =
+ ipmi::getService(*bus, powerControlIntf, powerControlObj);
+
+ ipmi::Value variant = ipmi::getDbusProperty(
+ *bus, service, powerControlObj, powerControlIntf, "PFail");
+ acFail = std::get<bool>(variant);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Failed to fetch PFail property",
+ entry("ERROR=%s", e.what()),
+ entry("PATH=%s", powerControlObj),
+ entry("INTERFACE=%s", powerControlIntf));
+ }
+ return acFail;
+}
} // namespace power_policy
+static std::optional<bool> getButtonEnabled(const std::string& buttonPath,
+ const std::string& buttonIntf)
+{
+ std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+ bool buttonDisabled = false;
+ try
+ {
+ auto service = ipmi::getService(*busp, buttonIntf, buttonPath);
+ ipmi::Value enabled = ipmi::getDbusProperty(*busp, service, buttonPath,
+ buttonIntf, "Enabled");
+ buttonDisabled = !std::get<bool>(enabled);
+ }
+ catch (sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Fail to get button Enabled property",
+ entry("PATH=%s", buttonPath.c_str()),
+ entry("ERROR=%s", e.what()));
+ return std::nullopt;
+ }
+ return std::make_optional(buttonDisabled);
+}
+
//----------------------------------------------------------------------
// Get Chassis Status commands
//----------------------------------------------------------------------
-ipmi_ret_t ipmi_get_chassis_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+ipmi::RspType<bool, // Power is on
+ bool, // Power overload
+ bool, // Interlock
+ bool, // power fault
+ bool, // power control fault
+ uint2_t, // power restore policy
+ bool, // reserved
+
+ bool, // AC failed
+ bool, // last power down caused by a Power overload
+ bool, // last power down caused by a power interlock
+ bool, // last power down caused by power fault
+ bool, // last ‘Power is on’ state was entered via IPMI command
+ uint3_t, // reserved
+
+ bool, // Chassis intrusion active
+ bool, // Front Panel Lockout active
+ bool, // Drive Fault
+ bool, // Cooling/fan fault detected
+ uint2_t, // Chassis Identify State
+ bool, // Chassis Identify command and state info supported
+ bool, // reserved
+
+ bool, // Power off button disabled
+ bool, // Reset button disabled
+ bool, // Diagnostic Interrupt button disabled
+ bool, // Standby (sleep) button disabled
+ bool, // Power off button disable allowed
+ bool, // Reset button disable allowed
+ bool, // Diagnostic Interrupt button disable allowed
+ bool // Standby (sleep) button disable allowed
+ >
+ ipmiGetChassisStatus()
{
- const char* objname = "/org/openbmc/control/power0";
- const char* intf = "org.openbmc.control.Power";
-
- sd_bus* bus = NULL;
- sd_bus_message* reply = NULL;
- int r = 0;
- int pgood = 0;
- char* busname = NULL;
- ipmi_ret_t rc = IPMI_CC_OK;
- ipmi_get_chassis_status_t chassis_status{};
-
- uint8_t s = 0;
-
using namespace chassis::internal;
- using namespace chassis::internal::cache;
- using namespace power_policy;
-
- const auto& powerRestoreSetting = objects.map.at(powerRestoreIntf).front();
- auto method = dbus.new_method_call(
- objects.service(powerRestoreSetting, powerRestoreIntf).c_str(),
- powerRestoreSetting.c_str(), ipmi::PROP_INTF, "Get");
- method.append(powerRestoreIntf, "PowerRestorePolicy");
- auto resp = dbus.call(method);
- if (resp.is_method_error())
+ std::optional<uint2_t> restorePolicy =
+ power_policy::getPowerRestorePolicy();
+ std::optional<bool> powerGood = power_policy::getPowerStatus();
+ if (!restorePolicy || !powerGood)
{
- log<level::ERR>("Error in PowerRestorePolicy Get");
- report<InternalFailure>();
- *data_len = 0;
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
- sdbusplus::message::variant<std::string> result;
- resp.read(result);
- auto powerRestore = RestorePolicy::convertPolicyFromString(
- variant_ns::get<std::string>(result));
-
- *data_len = 4;
-
- bus = ipmid_get_sd_bus_connection();
-
- r = mapper_get_service(bus, objname, &busname);
- if (r < 0)
- {
- log<level::ERR>("Failed to get bus name", entry("ERRNO=0x%X", -r));
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- goto finish;
- }
-
- r = sd_bus_get_property(bus, busname, objname, intf, "pgood", NULL, &reply,
- "i");
- if (r < 0)
- {
- log<level::ERR>("Failed to call sd_bus_get_property",
- entry("PROPERTY=%s", "pgood"), entry("ERRNO=0x%X", -r),
- entry("BUS=%s", busname), entry("PATH=%s", objname),
- entry("INTERFACE=%s", intf));
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- goto finish;
- }
-
- r = sd_bus_message_read(reply, "i", &pgood);
- if (r < 0)
- {
- log<level::ERR>("Failed to read sensor:", entry("ERRNO=0x%X", -r));
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- goto finish;
- }
-
- s = dbusToIpmi.at(powerRestore);
-
- // Current Power State
- // [7] reserved
- // [6..5] power restore policy
- // 00b = chassis stays powered off after AC/mains returns
- // 01b = after AC returns, power is restored to the state that was
- // in effect when AC/mains was lost.
- // 10b = chassis always powers up after AC/mains returns
- // 11b = unknow
- // Set to 00b, by observing the hardware behavior.
- // Do we need to define a dbus property to identify the restore
- // policy?
-
- // [4] power control fault
- // 1b = controller attempted to turn system power on or off, but
- // system did not enter desired state.
- // Set to 0b, since We don't support it..
-
- // [3] power fault
- // 1b = fault detected in main power subsystem.
- // set to 0b. for we don't support it.
-
- // [2] 1b = interlock (chassis is presently shut down because a chassis
- // panel interlock switch is active). (IPMI 1.5)
- // set to 0b, for we don't support it.
-
- // [1] power overload
- // 1b = system shutdown because of power overload condition.
- // set to 0b, for we don't support it.
-
- // [0] power is on
- // 1b = system power is on
- // 0b = system power is off(soft-off S4/S5, or mechanical off)
-
- chassis_status.cur_power_state = ((s & 0x3) << 5) | (pgood & 0x1);
-
- // Last Power Event
- // [7..5] – reserved
- // [4] – 1b = last ‘Power is on’ state was entered via IPMI command
- // [3] – 1b = last power down caused by power fault
- // [2] – 1b = last power down caused by a power interlock being activated
- // [1] – 1b = last power down caused by a Power overload
- // [0] – 1b = AC failed
- // set to 0x0, for we don't support these fields.
-
- chassis_status.last_power_event = 0;
-
- // Misc. Chassis State
- // [7] – reserved
- // [6] – 1b = Chassis Identify command and state info supported (Optional)
- // 0b = Chassis Identify command support unspecified via this command.
- // (The Get Command Support command , if implemented, would still
- // indicate support for the Chassis Identify command)
- // [5..4] – Chassis Identify State. Mandatory when bit[6] =1b, reserved
- // (return
- // as 00b) otherwise. Returns the present chassis identify state.
- // Refer to the Chassis Identify command for more info.
- // 00b = chassis identify state = Off
- // 01b = chassis identify state = Temporary(timed) On
- // 10b = chassis identify state = Indefinite On
- // 11b = reserved
- // [3] – 1b = Cooling/fan fault detected
- // [2] – 1b = Drive Fault
- // [1] – 1b = Front Panel Lockout active (power off and reset via chassis
- // push-buttons disabled.)
- // [0] – 1b = Chassis Intrusion active
- // set to 0, for we don't support them.
- chassis_status.misc_power_state = 0;
// Front Panel Button Capabilities and disable/enable status(Optional)
- // set to 0, for we don't support them.
- chassis_status.front_panel_button_cap_status = 0;
-
- // Pack the actual response
- std::memcpy(response, &chassis_status, *data_len);
+ std::optional<bool> powerButtonReading =
+ getButtonEnabled(powerButtonPath, powerButtonIntf);
+ // allow disable if the interface is present
+ bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
+ // default return the button is enabled (not disabled)
+ bool powerButtonDisabled = false;
+ if (powerButtonDisableAllow)
+ {
+ // return the real value of the button status, if present
+ powerButtonDisabled = *powerButtonReading;
+ }
-finish:
- free(busname);
- reply = sd_bus_message_unref(reply);
+ std::optional<bool> resetButtonReading =
+ getButtonEnabled(resetButtonPath, resetButtonIntf);
+ // allow disable if the interface is present
+ bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
+ // default return the button is enabled (not disabled)
+ bool resetButtonDisabled = false;
+ if (resetButtonDisableAllow)
+ {
+ // return the real value of the button status, if present
+ resetButtonDisabled = *resetButtonReading;
+ }
- return rc;
+ bool powerDownAcFailed = power_policy::getACFailStatus();
+
+ // This response has a lot of hard-coded, unsupported fields
+ // They are set to false or 0
+ constexpr bool powerOverload = false;
+ constexpr bool chassisInterlock = false;
+ constexpr bool powerFault = false;
+ constexpr bool powerControlFault = false;
+ constexpr bool powerDownOverload = false;
+ constexpr bool powerDownInterlock = false;
+ constexpr bool powerDownPowerFault = false;
+ constexpr bool powerStatusIPMI = false;
+ constexpr bool chassisIntrusionActive = false;
+ constexpr bool frontPanelLockoutActive = false;
+ constexpr bool driveFault = false;
+ constexpr bool coolingFanFault = false;
+ // chassisIdentifySupport set because this command is implemented
+ constexpr bool chassisIdentifySupport = true;
+ uint2_t chassisIdentifyState = static_cast<uint2_t>(chassisIDState);
+ constexpr bool diagButtonDisabled = false;
+ constexpr bool sleepButtonDisabled = false;
+ constexpr bool diagButtonDisableAllow = false;
+ constexpr bool sleepButtonDisableAllow = false;
+
+ return ipmi::responseSuccess(
+ *powerGood, powerOverload, chassisInterlock, powerFault,
+ powerControlFault, *restorePolicy,
+ false, // reserved
+
+ powerDownAcFailed, powerDownOverload, powerDownInterlock,
+ powerDownPowerFault, powerStatusIPMI,
+ uint3_t(0), // reserved
+
+ chassisIntrusionActive, frontPanelLockoutActive, driveFault,
+ coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
+ false, // reserved
+
+ powerButtonDisabled, resetButtonDisabled, diagButtonDisabled,
+ sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
+ diagButtonDisableAllow, sleepButtonDisableAllow);
}
//-------------------------------------------------------------
@@ -1018,25 +1129,16 @@ void indicate_no_softoff_needed()
std::ofstream(path.c_str());
}
-//----------------------------------------------------------------------
-// Chassis Control commands
-//----------------------------------------------------------------------
-ipmi_ret_t ipmi_chassis_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief Implementation of chassis control command
+ *
+ * @param - chassisControl command byte
+ *
+ * @return Success or InvalidFieldRequest.
+ */
+ipmi::RspType<> ipmiChassisControl(uint8_t chassisControl)
{
- // Error from power off.
int rc = 0;
-
- // No response for this command.
- *data_len = 0;
-
- // Catch the actual operaton by peeking into request buffer
- uint8_t chassis_ctrl_cmd = *(uint8_t*)request;
-
- switch (chassis_ctrl_cmd)
+ switch (chassisControl)
{
case CMD_POWER_ON:
rc = initiate_state_transition(State::Host::Transition::On);
@@ -1096,15 +1198,20 @@ ipmi_ret_t ipmi_chassis_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
rc = initiate_state_transition(State::Host::Transition::Off);
break;
+ case CMD_PULSE_DIAGNOSTIC_INTR:
+ rc = setNmiProperty(true);
+ break;
+
default:
{
log<level::ERR>("Invalid Chassis Control command",
- entry("CMD=0x%X", chassis_ctrl_cmd));
- rc = -1;
+ entry("CMD=0x%X", chassisControl));
+ return ipmi::responseInvalidFieldRequest();
}
}
- return ((rc < 0) ? IPMI_CC_INVALID : IPMI_CC_OK);
+ return ((rc < 0) ? ipmi::responseUnspecifiedError()
+ : ipmi::responseSuccess());
}
/** @brief Return D-Bus connection string to enclosure identify LED object
@@ -1159,7 +1266,7 @@ void enclosureIdentifyLed(bool flag)
dbus.new_method_call(connection.c_str(), identify_led_object_name,
"org.freedesktop.DBus.Properties", "Set");
led.append("xyz.openbmc_project.Led.Group", "Asserted",
- sdbusplus::message::variant<bool>(flag));
+ std::variant<bool>(flag));
auto ledReply = dbus.call(led);
if (ledReply.is_method_error())
{
@@ -1175,6 +1282,7 @@ void enclosureIdentifyLedOff()
{
try
{
+ chassisIDState = ChassisIDState::off;
enclosureIdentifyLed(false);
}
catch (const InternalFailure& e)
@@ -1207,6 +1315,7 @@ ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
identifyTimer->stop();
try
{
+ chassisIDState = ChassisIDState::temporaryOn;
enclosureIdentifyLed(true);
}
catch (const InternalFailure& e)
@@ -1217,6 +1326,7 @@ ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
if (forceIdentify)
{
+ chassisIDState = ChassisIDState::indefiniteOn;
return ipmi::responseSuccess();
}
// start the timer
@@ -1243,10 +1353,13 @@ std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
{0x01, Source::Sources::Network},
{0x02, Source::Sources::Disk},
{0x05, Source::Sources::ExternalMedia},
+ {0x0f, Source::Sources::RemovableMedia},
{ipmiDefault, Source::Sources::Default}};
std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
+#ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
{0x03, Mode::Modes::Safe},
+#endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
{0x06, Mode::Modes::Setup},
{ipmiDefault, Mode::Modes::Regular}};
@@ -1254,10 +1367,13 @@ std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
{Source::Sources::Network, 0x01},
{Source::Sources::Disk, 0x02},
{Source::Sources::ExternalMedia, 0x05},
+ {Source::Sources::RemovableMedia, 0x0f},
{Source::Sources::Default, ipmiDefault}};
std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
+#ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
{Mode::Modes::Safe, 0x03},
+#endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
{Mode::Modes::Setup, 0x06},
{Mode::Modes::Regular, ipmiDefault}};
@@ -1271,8 +1387,8 @@ static ipmi_ret_t setBootSource(const Source::Sources& source)
{
using namespace chassis::internal;
using namespace chassis::internal::cache;
- sdbusplus::message::variant<std::string> property =
- convertForMessage(source);
+ std::variant<std::string> property = convertForMessage(source);
+ settings::Objects& objects = getObjects();
auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
const auto& bootSourceSetting = std::get<settings::Path>(bootSetting);
auto method = dbus.new_method_call(
@@ -1297,7 +1413,8 @@ static ipmi_ret_t setBootMode(const Mode::Modes& mode)
{
using namespace chassis::internal;
using namespace chassis::internal::cache;
- sdbusplus::message::variant<std::string> property = convertForMessage(mode);
+ std::variant<std::string> property = convertForMessage(mode);
+ settings::Objects& objects = getObjects();
auto bootSetting = settings::boot::setting(objects, bootModeIntf);
const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
auto method = dbus.new_method_call(
@@ -1347,6 +1464,7 @@ ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
try
{
+ settings::Objects& objects = getObjects();
auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
const auto& bootSourceSetting =
std::get<settings::Path>(bootSetting);
@@ -1364,10 +1482,10 @@ ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
*data_len = 0;
return IPMI_CC_UNSPECIFIED_ERROR;
}
- sdbusplus::message::variant<std::string> result;
+ std::variant<std::string> result;
reply.read(result);
- auto bootSource = Source::convertSourcesFromString(
- variant_ns::get<std::string>(result));
+ auto bootSource =
+ Source::convertSourcesFromString(std::get<std::string>(result));
bootSetting = settings::boot::setting(objects, bootModeIntf);
const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
@@ -1384,8 +1502,8 @@ ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_UNSPECIFIED_ERROR;
}
reply.read(result);
- auto bootMode = Mode::convertModesFromString(
- variant_ns::get<std::string>(result));
+ auto bootMode =
+ Mode::convertModesFromString(std::get<std::string>(result));
bootOption = sourceDbusToIpmi.at(bootSource);
if ((Mode::Modes::Regular == bootMode) &&
@@ -1407,6 +1525,7 @@ ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
}
catch (InternalFailure& e)
{
+ cache::objectsPtr.reset();
report<InternalFailure>();
*data_len = 0;
return IPMI_CC_UNSPECIFIED_ERROR;
@@ -1489,6 +1608,8 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
(reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) ==
SET_PARM_BOOT_FLAGS_PERMANENT;
+ settings::Objects& objects = getObjects();
+
auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
oneTimeEnabled =
@@ -1548,9 +1669,17 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
setBootSource(Source::Sources::Default);
}
}
+ if ((modeIpmiToDbus.end() == modeItr) &&
+ (sourceIpmiToDbus.end() == sourceItr))
+ {
+ // return error if boot option is not supported
+ *data_len = 0;
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
}
catch (InternalFailure& e)
{
+ objectsPtr.reset();
report<InternalFailure>();
*data_len = 0;
return IPMI_CC_UNSPECIFIED_ERROR;
@@ -1588,74 +1717,57 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return rc;
}
-ipmi_ret_t ipmiGetPOHCounter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request, ipmi_response_t response,
- ipmi_data_len_t data_len, ipmi_context_t context)
+/** @brief implements Get POH counter command
+ * @parameter
+ * - none
+ * @returns IPMI completion code plus response data
+ * - minPerCount - Minutes per count
+ * - counterReading - counter reading
+ */
+ipmi::RspType<uint8_t, // Minutes per count
+ uint32_t // Counter reading
+ >
+ ipmiGetPOHCounter()
{
// sd_bus error
- ipmi_ret_t rc = IPMI_CC_OK;
-
- auto resptr = reinterpret_cast<GetPOHCountResponse*>(response);
-
try
{
- auto pohCounter = getPOHCounter();
- resptr->counterReading[0] = pohCounter;
- resptr->counterReading[1] = pohCounter >> 8;
- resptr->counterReading[2] = pohCounter >> 16;
- resptr->counterReading[3] = pohCounter >> 24;
+ return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount),
+ getPOHCounter());
}
catch (std::exception& e)
{
log<level::ERR>(e.what());
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
-
- resptr->minPerCount = poh::minutesPerCount;
- *data_len = sizeof(GetPOHCountResponse);
-
- return rc;
}
-ipmi_ret_t ipmi_chassis_set_power_restore_policy(
- ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
+ipmi::RspType<uint3_t, // policy support
+ uint5_t // reserved
+ >
+ ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield,
+ uint3_t policy, uint5_t reserved)
{
- auto* reqptr = reinterpret_cast<uint8_t*>(request);
- auto* resptr = reinterpret_cast<uint8_t*>(response);
- uint8_t reqPolicy = 0;
-
power_policy::DbusValue value =
power_policy::RestorePolicy::Policy::AlwaysOff;
- if (*data_len != power_policy::setPolicyReqLen)
+ if (reserved || (policy > power_policy::noChange))
{
- phosphor::logging::log<level::ERR>("Unsupported request length",
- entry("LEN=0x%x", *data_len));
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
+ phosphor::logging::log<level::ERR>(
+ "Reserved request parameter",
+ entry("REQ=0x%x", static_cast<int>(policy)));
+ return ipmi::responseInvalidFieldRequest();
}
- if (*reqptr > power_policy::noChange)
- {
- phosphor::logging::log<level::ERR>("Reserved request parameter",
- entry("REQ=0x%x", *reqptr));
- *data_len = 0;
- return IPMI_CC_PARM_OUT_OF_RANGE;
- }
-
- reqPolicy = *reqptr & power_policy::policyBitMask;
- if (reqPolicy == power_policy::noChange)
+ if (policy == power_policy::noChange)
{
// just return the supported policy
- *resptr = power_policy::allSupport;
- *data_len = power_policy::setPolicyReqLen;
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(power_policy::allSupport, reserved);
}
for (auto const& it : power_policy::dbusToIpmi)
{
- if (it.second == reqPolicy)
+ if (it.second == policy)
{
value = it.first;
break;
@@ -1664,57 +1776,51 @@ ipmi_ret_t ipmi_chassis_set_power_restore_policy(
try
{
+ settings::Objects& objects = chassis::internal::cache::getObjects();
const settings::Path& powerRestoreSetting =
- chassis::internal::cache::objects.map
- .at(chassis::internal::powerRestoreIntf)
- .front();
- sdbusplus::message::variant<std::string> property =
- convertForMessage(value);
-
- auto method = chassis::internal::dbus.new_method_call(
- chassis::internal::cache::objects
+ objects.map.at(chassis::internal::powerRestoreIntf).front();
+ std::variant<std::string> property = convertForMessage(value);
+
+ auto sdbusp = getSdBus();
+ boost::system::error_code ec;
+ sdbusp->yield_method_call<void>(
+ yield, ec,
+ objects
.service(powerRestoreSetting,
chassis::internal::powerRestoreIntf)
.c_str(),
- powerRestoreSetting.c_str(), ipmi::PROP_INTF, "Set");
-
- method.append(chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
- property);
- auto reply = chassis::internal::dbus.call(method);
- if (reply.is_method_error())
+ powerRestoreSetting, ipmi::PROP_INTF, "Set",
+ chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
+ property);
+ if (ec)
{
phosphor::logging::log<level::ERR>("Unspecified Error");
- *data_len = 0;
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
}
catch (InternalFailure& e)
{
+ chassis::internal::cache::objectsPtr.reset();
report<InternalFailure>();
- *data_len = 0;
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
- *resptr = power_policy::allSupport;
- *data_len = power_policy::setPolicyReqLen;
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(power_policy::allSupport, reserved);
}
void register_netfn_chassis_functions()
{
createIdentifyTimer();
- // <Wildcard Command>
- ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL,
- ipmi_chassis_wildcard, PRIVILEGE_USER);
-
// Get Chassis Capabilities
- ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL,
- ipmi_get_chassis_cap, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
+ ipmi::chassis::cmdGetChassisCapabilities,
+ ipmi::Privilege::User, ipmiGetChassisCap);
// Set Chassis Capabilities
- ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_CHASSIS_CAP, NULL,
- ipmi_set_chassis_cap, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
+ ipmi::chassis::cmdSetChassisCapabilities,
+ ipmi::Privilege::User, ipmiSetChassisCap);
// <Get System Boot Options>
ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL,
@@ -1722,12 +1828,14 @@ void register_netfn_chassis_functions()
PRIVILEGE_OPERATOR);
// <Get Chassis Status>
- ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_STATUS, NULL,
- ipmi_get_chassis_status, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
+ ipmi::chassis::cmdGetChassisStatus,
+ ipmi::Privilege::User, ipmiGetChassisStatus);
// <Chassis Control>
- ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL, NULL,
- ipmi_chassis_control, PRIVILEGE_OPERATOR);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
+ ipmi::chassis::cmdChassisControl,
+ ipmi::Privilege::Operator, ipmiChassisControl);
// <Chassis Identify>
ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
@@ -1739,11 +1847,13 @@ void register_netfn_chassis_functions()
ipmi_chassis_set_sys_boot_options,
PRIVILEGE_OPERATOR);
// <Get POH Counter>
- ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_POH_COUNTER, NULL,
- ipmiGetPOHCounter, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
+ ipmi::chassis::cmdGetPohCounter,
+ ipmi::Privilege::User, ipmiGetPOHCounter);
// <Set Power Restore Policy>
- ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_RESTORE_POLICY, NULL,
- ipmi_chassis_set_power_restore_policy,
- PRIVILEGE_OPERATOR);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
+ ipmi::chassis::cmdSetPowerRestorePolicy,
+ ipmi::Privilege::Operator,
+ ipmiChassisSetPowerRestorePolicy);
}
diff --git a/chassishandler.hpp b/chassishandler.hpp
index 49b5ef8..dcaf06c 100644
--- a/chassishandler.hpp
+++ b/chassishandler.hpp
@@ -14,8 +14,6 @@ enum ipmi_netfn_chassis_cmds
IPMI_CMD_CHASSIS_CONTROL = 0x02,
IPMI_CMD_CHASSIS_IDENTIFY = 0x04,
IPMI_CMD_SET_CHASSIS_CAP = 0x05,
- // Set Power Restore Policy
- IPMI_CMD_SET_RESTORE_POLICY = 0x06,
// Get capability bits
IPMI_CMD_SET_SYS_BOOT_OPTIONS = 0x08,
IPMI_CMD_GET_SYS_BOOT_OPTIONS = 0x09,
@@ -58,3 +56,11 @@ enum class BootOptionResponseSize : size_t
BOOT_FLAGS = 5,
OPAL_NETWORK_SETTINGS = 50
};
+
+enum class ChassisIDState : uint8_t
+{
+ off = 0x0,
+ temporaryOn = 0x1,
+ indefiniteOn = 0x2,
+ reserved = 0x3
+};
diff --git a/configure.ac b/configure.ac
index 2bb011f..29f58e2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,6 +25,28 @@ AM_PROG_AR
AC_PROG_INSTALL
AC_PROG_MAKE_SET
+# Add an option to enable/disable safe mode in boot flags
+AC_ARG_ENABLE([boot-flag-safe-mode-support],
+ AS_HELP_STRING([--disable-boot-flag-safe-mode-support], [Disable safe mode option in boot flags. [default=enable]])
+)
+AS_IF([test "x$enable_boot_flag_safe_mode_support" != "xno"],
+ AC_MSG_NOTICE([Enabling safe mode option in boot flags])
+ [cpp_flags="-DENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT"]
+ AC_SUBST([CPPFLAGS], [$cpp_flags]),
+ AC_MSG_WARN([Disabling safe mode option in boot flags])
+)
+
+# Add an option to enable/disable i2c master write read command white list checking
+AC_ARG_ENABLE([i2c-whitelist-check],
+ AS_HELP_STRING([--disable-i2c-whitelist-check], [Disable I2C master write read command white list check. [default=enable]])
+)
+AS_IF([test "x$enable_i2c_whitelist_check" != "xno"],
+ AC_MSG_NOTICE([Enabling I2C master write read command white list check])
+ [cpp_flags="-DENABLE_I2C_WHITELIST_CHECK"]
+ AC_SUBST([CPPFLAGS], [$cpp_flags]),
+ AC_MSG_WARN([Disabling I2C master write read command white list check])
+)
+
# softoff dir specific ones
AC_ARG_ENABLE([softoff],
AS_HELP_STRING([--enable-softoff], [Builds soft power off])
@@ -103,20 +125,24 @@ if test -z "$WHITELIST_CONF"; then
WHITELIST_CONF=${srcdir}/host-ipmid-whitelist.conf
fi
-AS_IF([test "x$SENSOR_YAML_GEN" == "x"], [SENSOR_YAML_GEN="sensor-example.yaml"])
+AS_IF([test "x$SENSOR_YAML_GEN" == "x"], [SENSOR_YAML_GEN="$srcdir/scripts/sensor-example.yaml"])
SENSORGEN="$PYTHON ${srcdir}/scripts/sensor_gen.py -i $SENSOR_YAML_GEN"
+AC_SUBST(SENSOR_YAML_GEN)
AC_SUBST(SENSORGEN)
-AS_IF([test "x$INVSENSOR_YAML_GEN" == "x"], [INVSENSOR_YAML_GEN="inventory-sensor-example.yaml"])
+AS_IF([test "x$INVSENSOR_YAML_GEN" == "x"], [INVSENSOR_YAML_GEN="$srcdir/scripts/inventory-sensor-example.yaml"])
INVSENSORGEN="$PYTHON ${srcdir}/scripts/inventory-sensor.py -i $INVSENSOR_YAML_GEN"
+AC_SUBST(INVSENSOR_YAML_GEN)
AC_SUBST(INVSENSORGEN)
-AS_IF([test "x$FRU_YAML_GEN" == "x"], [FRU_YAML_GEN="fru-read-example.yaml"])
+AS_IF([test "x$FRU_YAML_GEN" == "x"], [FRU_YAML_GEN="$srcdir/scripts/fru-read-example.yaml"])
FRUGEN="$PYTHON $srcdir/scripts/fru_gen.py -i $FRU_YAML_GEN"
+AC_SUBST(FRU_YAML_GEN)
AC_SUBST(FRUGEN)
-AS_IF([test "x$ENTITY_YAML_GEN" == "x"], [ENTITY_YAML_GEN="entity-example.yaml"])
+AS_IF([test "x$ENTITY_YAML_GEN" == "x"], [ENTITY_YAML_GEN="$srcdir/scripts/entity-example.yaml"])
ENTITYGEN="$PYTHON $srcdir/scripts/entity_gen.py -i $ENTITY_YAML_GEN"
+AC_SUBST(ENTITY_YAML_GEN)
AC_SUBST(ENTITYGEN)
AC_DEFINE(CALLOUT_FWD_ASSOCIATION, "callout", [The name of the callout's forward association.])
@@ -188,6 +214,58 @@ AC_ARG_VAR(HOST_IPMI_LIB_PATH, [The file path to search for libraries.])
AS_IF([test "x$HOST_IPMI_LIB_PATH" == "x"], [HOST_IPMI_LIB_PATH="/usr/lib/ipmid-providers/"])
AC_DEFINE_UNQUOTED([HOST_IPMI_LIB_PATH], ["$HOST_IPMI_LIB_PATH"], [The file path to search for libraries.])
+# When a sensor read fails, hwmon will update the OperationalState interface's Functional property.
+# This will mark the sensor as not functional and we will skip reading from that sensor.
+AC_ARG_ENABLE([update-functional-on-fail],
+ AS_HELP_STRING(
+ [--enable-update-functional-on-fail],
+ [Check functional property to skip reading from faulty sensors.]
+ )
+)
+
+AC_ARG_VAR(UPDATE_FUNCTIONAL_ON_FAIL, [Check functional property to skip reading from faulty sensors.])
+AS_IF(
+ [test "x$enable_update_functional_on_fail" == "xyes"],
+ [UPDATE_FUNCTIONAL_ON_FAIL="yes"]
+ AC_DEFINE_UNQUOTED(
+ [UPDATE_FUNCTIONAL_ON_FAIL],
+ ["$UPDATE_FUNCTIONAL_ON_FAIL"],
+ [Check functional property to skip reading from faulty sensors.]
+ )
+)
+
+# When disable-libuserlayer flag is set, libuserlayer won't be included in the build.
+AC_ARG_ENABLE([libuserlayer],
+ AS_HELP_STRING([--disable-libuserlayer], [Set a flag to exclude libuserlayer])
+)
+AM_CONDITIONAL(FEATURE_LIBUSERLAYER, [test "x$enable_libuserlayer" != "xno"])
+
+# When enable-transport-oem flag is set, the transporthandler_oem.cpp contents
+# are compiled and added to the project. The transporthandler_oem.cpp file is
+# copied from your own customization layer in the
+# phosphor-ipmi-host_%.bbappend file. It is not necessary to create this file
+# unless OEM Parameter extensions are required.
+AC_ARG_ENABLE([transport_oem],
+ [ --enable-transport-oem Enable/disable OEM Parameter extensions],
+ [case "${enableval}" in
+ yes) transport_oem=true ;;
+ no) transport_oem=false ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-transport_oem]) ;;
+ esac],[transport_oem=false]
+ )
+AM_CONDITIONAL([FEATURE_TRANSPORT_OEM], [test x$transport_oem = xtrue])
+
+# IPMI whitelist mechanism is not needed by everyone; offer a way to disable it
+AC_ARG_ENABLE([ipmi-whitelist],
+ [ --enable-ipmi-whitelist Enable/disable IPMI whitelist filtering],
+ [case "${enableval}" in
+ yes) ipmi_whitelist=true ;;
+ no) ipmi_whitelist=false ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-ipmi-whitelist]) ;;
+ esac],[ipmi_whitelist=true]
+ )
+AM_CONDITIONAL([FEATURE_IPMI_WHITELIST], [test x$ipmi_whitelist = xtrue])
+
# Create configured output
AC_CONFIG_FILES([
Makefile
diff --git a/dcmihandler.cpp b/dcmihandler.cpp
index 3188585..f8498f5 100644
--- a/dcmihandler.cpp
+++ b/dcmihandler.cpp
@@ -13,7 +13,7 @@
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus.hpp>
-#include <sdbusplus/message/types.hpp>
+#include <variant>
#include <xyz/openbmc_project/Common/error.hpp>
using namespace phosphor::logging;
@@ -37,8 +37,8 @@ constexpr auto DCMI_OPTION_60_43_MASK = 0x02;
constexpr auto DCMI_OPTION_12_MASK = 0x01;
constexpr auto DCMI_ACTIVATE_DHCP_MASK = 0x01;
constexpr auto DCMI_ACTIVATE_DHCP_REPLY = 0x00;
-constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE = 0x05;
-constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE = 0x04;
+constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE = 0x04;
+constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE = 0x03;
constexpr auto DHCP_TIMING1 = 0x04; // 4 sec
constexpr auto DHCP_TIMING2_UPPER = 0x00; // 2 min
constexpr auto DHCP_TIMING2_LOWER = 0x78;
@@ -54,7 +54,6 @@ constexpr auto SENSOR_VALUE_PROP = "Value";
constexpr auto SENSOR_SCALE_PROP = "Scale";
using namespace phosphor::logging;
-namespace variant_ns = sdbusplus::message::variant_ns;
namespace dcmi
{
@@ -86,10 +85,10 @@ uint32_t getPcap(sdbusplus::bus::bus& bus)
log<level::ERR>("Error in getPcap prop");
elog<InternalFailure>();
}
- sdbusplus::message::variant<uint32_t> pcap;
+ std::variant<uint32_t> pcap;
reply.read(pcap);
- return variant_ns::get<uint32_t>(pcap);
+ return std::get<uint32_t>(pcap);
}
bool getPcapEnabled(sdbusplus::bus::bus& bus)
@@ -107,10 +106,10 @@ bool getPcapEnabled(sdbusplus::bus::bus& bus)
log<level::ERR>("Error in getPcapEnabled prop");
elog<InternalFailure>();
}
- sdbusplus::message::variant<bool> pcapEnabled;
+ std::variant<bool> pcapEnabled;
reply.read(pcapEnabled);
- return variant_ns::get<bool>(pcapEnabled);
+ return std::get<bool>(pcapEnabled);
}
void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap)
@@ -121,7 +120,7 @@ void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap)
"org.freedesktop.DBus.Properties", "Set");
method.append(PCAP_INTERFACE, POWER_CAP_PROP);
- method.append(sdbusplus::message::variant<uint32_t>(powerCap));
+ method.append(std::variant<uint32_t>(powerCap));
auto reply = bus.call(method);
@@ -140,7 +139,7 @@ void setPcapEnable(sdbusplus::bus::bus& bus, bool enabled)
"org.freedesktop.DBus.Properties", "Set");
method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP);
- method.append(sdbusplus::message::variant<bool>(enabled));
+ method.append(std::variant<bool>(enabled));
auto reply = bus.call(method);
@@ -206,10 +205,10 @@ std::string readAssetTag()
elog<InternalFailure>();
}
- sdbusplus::message::variant<std::string> assetTag;
+ std::variant<std::string> assetTag;
reply.read(assetTag);
- return variant_ns::get<std::string>(assetTag);
+ return std::get<std::string>(assetTag);
}
void writeAssetTag(const std::string& assetTag)
@@ -226,7 +225,7 @@ void writeAssetTag(const std::string& assetTag)
(objectTree.begin()->first).c_str(), dcmi::propIntf, "Set");
method.append(dcmi::assetTagIntf);
method.append(dcmi::assetTagProp);
- method.append(sdbusplus::message::variant<std::string>(assetTag));
+ method.append(std::variant<std::string>(assetTag));
auto reply = bus.call(method);
if (reply.is_method_error())
@@ -244,7 +243,7 @@ std::string getHostName(void)
auto value = ipmi::getDbusProperty(bus, service, networkConfigObj,
networkConfigIntf, hostNameProp);
- return variant_ns::get<std::string>(value);
+ return std::get<std::string>(value);
}
bool getDHCPEnabled()
@@ -258,7 +257,7 @@ bool getDHCPEnabled()
auto value = ipmi::getDbusProperty(bus, service, ethernetObj.first,
ethernetIntf, "DHCPEnabled");
- return variant_ns::get<bool>(value);
+ return std::get<bool>(value);
}
bool getDHCPOption(std::string prop)
@@ -268,7 +267,7 @@ bool getDHCPOption(std::string prop)
auto service = ipmi::getService(bus, dhcpIntf, dhcpObj);
auto value = ipmi::getDbusProperty(bus, service, dhcpObj, dhcpIntf, prop);
- return variant_ns::get<bool>(value);
+ return std::get<bool>(value);
}
void setDHCPOption(std::string prop, bool value)
@@ -311,18 +310,10 @@ ipmi_ret_t getPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_INVALID;
}
- auto requestData =
- reinterpret_cast<const dcmi::GetPowerLimitRequest*>(request);
std::vector<uint8_t> outPayload(sizeof(dcmi::GetPowerLimitResponse));
auto responseData =
reinterpret_cast<dcmi::GetPowerLimitResponse*>(outPayload.data());
- if (requestData->groupID != dcmi::groupExtId)
- {
- *data_len = 0;
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
-
sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()};
uint32_t pcapValue = 0;
bool pcapEnable = false;
@@ -338,8 +329,6 @@ ipmi_ret_t getPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_UNSPECIFIED_ERROR;
}
- responseData->groupID = dcmi::groupExtId;
-
/*
* Exception action if power limit is exceeded and cannot be controlled
* with the correction time limit is hardcoded to Hard Power Off system
@@ -381,15 +370,6 @@ ipmi_ret_t setPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
auto requestData =
reinterpret_cast<const dcmi::SetPowerLimitRequest*>(request);
- std::vector<uint8_t> outPayload(sizeof(dcmi::SetPowerLimitResponse));
- auto responseData =
- reinterpret_cast<dcmi::SetPowerLimitResponse*>(outPayload.data());
-
- if (requestData->groupID != dcmi::groupExtId)
- {
- *data_len = 0;
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()};
@@ -407,10 +387,7 @@ ipmi_ret_t setPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
log<level::INFO>("Set Power Cap",
entry("POWERCAP=%u", requestData->powerLimit));
- responseData->groupID = dcmi::groupExtId;
- memcpy(response, outPayload.data(), outPayload.size());
- *data_len = outPayload.size();
-
+ *data_len = 0;
return IPMI_CC_OK;
}
@@ -427,15 +404,6 @@ ipmi_ret_t applyPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
auto requestData =
reinterpret_cast<const dcmi::ApplyPowerLimitRequest*>(request);
- std::vector<uint8_t> outPayload(sizeof(dcmi::ApplyPowerLimitResponse));
- auto responseData =
- reinterpret_cast<dcmi::ApplyPowerLimitResponse*>(outPayload.data());
-
- if (requestData->groupID != dcmi::groupExtId)
- {
- *data_len = 0;
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()};
@@ -453,10 +421,7 @@ ipmi_ret_t applyPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
log<level::INFO>("Set Power Cap Enable",
entry("POWERCAPENABLE=%u", requestData->powerLimitAction));
- responseData->groupID = dcmi::groupExtId;
- memcpy(response, outPayload.data(), outPayload.size());
- *data_len = outPayload.size();
-
+ *data_len = 0;
return IPMI_CC_OK;
}
@@ -470,12 +435,6 @@ ipmi_ret_t getAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
auto responseData =
reinterpret_cast<dcmi::GetAssetTagResponse*>(outPayload.data());
- if (requestData->groupID != dcmi::groupExtId)
- {
- *data_len = 0;
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
-
// Verify offset to read and number of bytes to read are not exceeding the
// range.
if ((requestData->offset > dcmi::assetTagMaxOffset) ||
@@ -498,8 +457,6 @@ ipmi_ret_t getAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_UNSPECIFIED_ERROR;
}
- responseData->groupID = dcmi::groupExtId;
-
// Return if the asset tag is not populated.
if (!assetTag.size())
{
@@ -545,12 +502,6 @@ ipmi_ret_t setAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
auto responseData =
reinterpret_cast<dcmi::SetAssetTagResponse*>(outPayload.data());
- if (requestData->groupID != dcmi::groupExtId)
- {
- *data_len = 0;
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
-
// Verify offset to read and number of bytes to read are not exceeding the
// range.
if ((requestData->offset > dcmi::assetTagMaxOffset) ||
@@ -581,7 +532,6 @@ ipmi_ret_t setAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
dcmi::writeAssetTag(assetTag);
- responseData->groupID = dcmi::groupExtId;
responseData->tagLength = assetTag.size();
memcpy(response, outPayload.data(), outPayload.size());
*data_len = outPayload.size();
@@ -607,8 +557,7 @@ ipmi_ret_t getMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
*data_len = 0;
- if (requestData->groupID != dcmi::groupExtId ||
- requestData->bytes > dcmi::maxBytes ||
+ if (requestData->bytes > dcmi::maxBytes ||
requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen)
{
return IPMI_CC_INVALID_FIELD_REQUEST;
@@ -630,7 +579,6 @@ ipmi_ret_t getMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
auto responseStr = hostName.substr(requestData->offset, requestData->bytes);
auto responseStrLen = std::min(static_cast<std::size_t>(requestData->bytes),
responseStr.length() + 1);
- responseData->groupID = dcmi::groupExtId;
responseData->strLen = hostName.length();
std::copy(begin(responseStr), end(responseStr), responseData->data);
@@ -651,8 +599,7 @@ ipmi_ret_t setMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
*data_len = 0;
- if (requestData->groupID != dcmi::groupExtId ||
- requestData->bytes > dcmi::maxBytes ||
+ if (requestData->bytes > dcmi::maxBytes ||
requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen + 1 ||
(requestData->offset + requestData->bytes ==
dcmi::maxCtrlIdStrLen + 1 &&
@@ -701,7 +648,6 @@ ipmi_ret_t setMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_UNSPECIFIED_ERROR;
}
- responseData->groupID = dcmi::groupExtId;
responseData->offset = requestData->offset + requestData->bytes;
*data_len = sizeof(*responseData);
return IPMI_CC_OK;
@@ -768,12 +714,6 @@ ipmi_ret_t getDCMICapabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_INVALID_FIELD_REQUEST;
}
- if (requestData->groupID != dcmi::groupExtId)
- {
- *data_len = 0;
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
-
auto responseData = reinterpret_cast<dcmi::GetDCMICapResponse*>(response);
// For each capabilities in a parameter fill the data from
@@ -808,7 +748,6 @@ ipmi_ret_t getDCMICapabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
}
}
- responseData->groupID = dcmi::groupExtId;
responseData->major = DCMI_SPEC_MAJOR_VERSION;
responseData->minor = DCMI_SPEC_MINOR_VERSION;
responseData->paramRevision = DCMI_PARAMETER_REVISION;
@@ -834,16 +773,15 @@ Temperature readTemp(const std::string& dbusService,
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
auto result = ipmi::getAllDbusProperties(
bus, dbusService, dbusPath, "xyz.openbmc_project.Sensor.Value");
- auto temperature = sdbusplus::message::variant_ns::visit(
- ipmi::VariantToDoubleVisitor(), result.at("Value"));
+ auto temperature =
+ std::visit(ipmi::VariantToDoubleVisitor(), result.at("Value"));
double absTemp = std::abs(temperature);
auto findFactor = result.find("Scale");
double factor = 0.0;
if (findFactor != result.end())
{
- factor = sdbusplus::message::variant_ns::visit(
- ipmi::VariantToDoubleVisitor(), findFactor->second);
+ factor = std::visit(ipmi::VariantToDoubleVisitor(), findFactor->second);
}
double scale = std::pow(10, factor);
@@ -997,13 +935,6 @@ ipmi_ret_t getTempReadings(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_INVALID_FIELD_REQUEST;
}
- if (requestData->groupID != dcmi::groupExtId)
- {
- log<level::ERR>("Invalid Group ID",
- entry("GROUP_ID=%d", requestData->groupID));
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
-
if (requestData->sensorType != dcmi::temperatureSensorType)
{
log<level::ERR>("Invalid sensor type",
@@ -1036,7 +967,6 @@ ipmi_ret_t getTempReadings(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_UNSPECIFIED_ERROR;
}
- responseData->groupID = dcmi::groupExtId;
size_t payloadSize = temps.size() * sizeof(dcmi::temp_readings::Response);
if (!temps.empty())
{
@@ -1084,8 +1014,8 @@ int64_t getPowerReading(sdbusplus::bus::bus& bus)
// Read the sensor value and scale properties
auto properties = ipmi::getAllDbusProperties(bus, service, objectPath,
SENSOR_VALUE_INTF);
- auto value = variant_ns::get<int64_t>(properties[SENSOR_VALUE_PROP]);
- auto scale = variant_ns::get<int64_t>(properties[SENSOR_SCALE_PROP]);
+ auto value = std::get<int64_t>(properties[SENSOR_VALUE_PROP]);
+ auto scale = std::get<int64_t>(properties[SENSOR_SCALE_PROP]);
// Power reading needs to be scaled with the Scale value using the
// formula Value * 10^Scale.
@@ -1106,19 +1036,15 @@ ipmi_ret_t setDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
{
auto requestData =
reinterpret_cast<const dcmi::SetConfParamsRequest*>(request);
- auto responseData =
- reinterpret_cast<dcmi::SetConfParamsResponse*>(response);
- if (requestData->groupID != dcmi::groupExtId ||
- *data_len < DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE ||
+ if (*data_len < DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE ||
*data_len > DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE)
{
- log<level::ERR>("Invalid Group ID or Invalid Requested Packet size",
- entry("GROUP_ID=%d", requestData->groupID),
+ log<level::ERR>("Invalid Requested Packet size",
entry("PACKET SIZE=%d", *data_len));
+ *data_len = 0;
return IPMI_CC_INVALID_FIELD_REQUEST;
}
-
*data_len = 0;
try
@@ -1170,9 +1096,6 @@ ipmi_ret_t setDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
log<level::ERR>(e.what());
return IPMI_CC_UNSPECIFIED_ERROR;
}
- responseData->groupID = dcmi::groupExtId;
- *data_len = sizeof(dcmi::SetConfParamsResponse);
-
return IPMI_CC_OK;
}
@@ -1188,11 +1111,9 @@ ipmi_ret_t getDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
responseData->data[0] = 0x00;
- if (requestData->groupID != dcmi::groupExtId ||
- *data_len != sizeof(dcmi::GetConfParamsRequest))
+ if (*data_len != sizeof(dcmi::GetConfParamsRequest))
{
- log<level::ERR>("Invalid Group ID or Invalid Requested Packet size",
- entry("GROUP_ID=%d", requestData->groupID),
+ log<level::ERR>("Invalid Requested Packet size",
entry("PACKET SIZE=%d", *data_len));
return IPMI_CC_INVALID_FIELD_REQUEST;
}
@@ -1242,7 +1163,6 @@ ipmi_ret_t getDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_UNSPECIFIED_ERROR;
}
- responseData->groupID = dcmi::groupExtId;
responseData->major = DCMI_SPEC_MAJOR_VERSION;
responseData->minor = DCMI_SPEC_MINOR_VERSION;
responseData->paramRevision = DCMI_CONFIG_PARAMETER_REVISION;
@@ -1262,17 +1182,9 @@ ipmi_ret_t getPowerReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
}
ipmi_ret_t rc = IPMI_CC_OK;
- auto requestData =
- reinterpret_cast<const dcmi::GetPowerReadingRequest*>(request);
auto responseData =
reinterpret_cast<dcmi::GetPowerReadingResponse*>(response);
- if (requestData->groupID != dcmi::groupExtId)
- {
- *data_len = 0;
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
-
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
int64_t power = 0;
try
@@ -1286,7 +1198,6 @@ ipmi_ret_t getPowerReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
entry("PROPERTY=%s", SENSOR_VALUE_PROP));
return IPMI_CC_UNSPECIFIED_ERROR;
}
- responseData->groupID = dcmi::groupExtId;
// TODO: openbmc/openbmc#2819
// Minimum, Maximum, Average power, TimeFrame, TimeStamp,
@@ -1427,13 +1338,6 @@ ipmi_ret_t getSensorInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_INVALID_FIELD_REQUEST;
}
- if (requestData->groupID != dcmi::groupExtId)
- {
- log<level::ERR>("Invalid Group ID",
- entry("GROUP_ID=%d", requestData->groupID));
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
-
if (requestData->sensorType != dcmi::temperatureSensorType)
{
log<level::ERR>("Invalid sensor type",
@@ -1475,7 +1379,6 @@ ipmi_ret_t getSensorInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_UNSPECIFIED_ERROR;
}
- responseData->groupID = dcmi::groupExtId;
size_t payloadSize = sensors.size() * sizeof(dcmi::sensor_info::Response);
if (!sensors.empty())
{
diff --git a/dcmihandler.hpp b/dcmihandler.hpp
index a188c25..4f35bc6 100644
--- a/dcmihandler.hpp
+++ b/dcmihandler.hpp
@@ -129,9 +129,8 @@ static constexpr size_t maxCtrlIdStrLen = 63;
*/
struct GetAssetTagRequest
{
- uint8_t groupID; //!< Group extension identification.
- uint8_t offset; //!< Offset to read.
- uint8_t bytes; //!< Number of bytes to read.
+ uint8_t offset; //!< Offset to read.
+ uint8_t bytes; //!< Number of bytes to read.
} __attribute__((packed));
/** @struct GetAssetTagResponse
@@ -140,7 +139,6 @@ struct GetAssetTagRequest
*/
struct GetAssetTagResponse
{
- uint8_t groupID; //!< Group extension identification.
uint8_t tagLength; //!< Total asset tag length.
} __attribute__((packed));
@@ -150,9 +148,8 @@ struct GetAssetTagResponse
*/
struct SetAssetTagRequest
{
- uint8_t groupID; //!< Group extension identification.
- uint8_t offset; //!< Offset to write.
- uint8_t bytes; //!< Number of bytes to write.
+ uint8_t offset; //!< Offset to write.
+ uint8_t bytes; //!< Number of bytes to write.
} __attribute__((packed));
/** @struct SetAssetTagResponse
@@ -161,7 +158,6 @@ struct SetAssetTagRequest
*/
struct SetAssetTagResponse
{
- uint8_t groupID; //!< Group extension identification.
uint8_t tagLength; //!< Total asset tag length.
} __attribute__((packed));
@@ -211,23 +207,12 @@ uint32_t getPcap(sdbusplus::bus::bus& bus);
*/
bool getPcapEnabled(sdbusplus::bus::bus& bus);
-/** @struct GetPowerLimitRequest
- *
- * DCMI payload for Get Power Limit command request.
- */
-struct GetPowerLimitRequest
-{
- uint8_t groupID; //!< Group extension identification.
- uint16_t reserved; //!< Reserved
-} __attribute__((packed));
-
/** @struct GetPowerLimitResponse
*
* DCMI payload for Get Power Limit command response.
*/
struct GetPowerLimitResponse
{
- uint8_t groupID; //!< Group extension identification.
uint16_t reserved; //!< Reserved.
uint8_t exceptionAction; //!< Exception action.
uint16_t powerLimit; //!< Power limit requested in watts.
@@ -249,7 +234,6 @@ void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap);
*/
struct SetPowerLimitRequest
{
- uint8_t groupID; //!< Group extension identification.
uint16_t reserved; //!< Reserved
uint8_t reserved1; //!< Reserved
uint8_t exceptionAction; //!< Exception action.
@@ -259,15 +243,6 @@ struct SetPowerLimitRequest
uint16_t samplingPeriod; //!< Statistics sampling period in seconds.
} __attribute__((packed));
-/** @struct SetPowerLimitResponse
- *
- * DCMI payload for Set Power Limit command response.
- */
-struct SetPowerLimitResponse
-{
- uint8_t groupID; //!< Group extension identification.
-} __attribute__((packed));
-
/** @brief Enable or disable the power capping
*
* @param[in] bus - dbus connection
@@ -281,29 +256,18 @@ void setPcapEnable(sdbusplus::bus::bus& bus, bool enabled);
*/
struct ApplyPowerLimitRequest
{
- uint8_t groupID; //!< Group extension identification.
uint8_t powerLimitAction; //!< Power limit activation
uint16_t reserved; //!< Reserved
} __attribute__((packed));
-/** @struct ApplyPowerLimitResponse
- *
- * DCMI payload for Acticate/Deactivate Power Limit command response.
- */
-struct ApplyPowerLimitResponse
-{
- uint8_t groupID; //!< Group extension identification.
-} __attribute__((packed));
-
/** @struct GetMgmntCtrlIdStrRequest
*
* DCMI payload for Get Management Controller Identifier String cmd request.
*/
struct GetMgmntCtrlIdStrRequest
{
- uint8_t groupID; //!< Group extension identification.
- uint8_t offset; //!< Offset to read.
- uint8_t bytes; //!< Number of bytes to read.
+ uint8_t offset; //!< Offset to read.
+ uint8_t bytes; //!< Number of bytes to read.
} __attribute__((packed));
/** @struct GetMgmntCtrlIdStrResponse
@@ -312,9 +276,8 @@ struct GetMgmntCtrlIdStrRequest
*/
struct GetMgmntCtrlIdStrResponse
{
- uint8_t groupID; //!< Group extension identification.
- uint8_t strLen; //!< ID string length.
- char data[]; //!< ID string
+ uint8_t strLen; //!< ID string length.
+ char data[]; //!< ID string
} __attribute__((packed));
/** @struct SetMgmntCtrlIdStrRequest
@@ -323,10 +286,9 @@ struct GetMgmntCtrlIdStrResponse
*/
struct SetMgmntCtrlIdStrRequest
{
- uint8_t groupID; //!< Group extension identification.
- uint8_t offset; //!< Offset to write.
- uint8_t bytes; //!< Number of bytes to read.
- char data[]; //!< ID string
+ uint8_t offset; //!< Offset to write.
+ uint8_t bytes; //!< Number of bytes to read.
+ char data[]; //!< ID string
} __attribute__((packed));
/** @struct GetMgmntCtrlIdStrResponse
@@ -335,8 +297,7 @@ struct SetMgmntCtrlIdStrRequest
*/
struct SetMgmntCtrlIdStrResponse
{
- uint8_t groupID; //!< Group extension identification.
- uint8_t offset; //!< Last Offset Written.
+ uint8_t offset; //!< Last Offset Written.
} __attribute__((packed));
/** @enum DCMICapParameters
@@ -357,8 +318,7 @@ enum class DCMICapParameters
*/
struct GetDCMICapRequest
{
- uint8_t groupID; //!< Group extension identification.
- uint8_t param; //!< Capability parameter selector.
+ uint8_t param; //!< Capability parameter selector.
} __attribute__((packed));
/** @struct GetDCMICapRequest
@@ -367,7 +327,6 @@ struct GetDCMICapRequest
*/
struct GetDCMICapResponse
{
- uint8_t groupID; //!< Group extension identification.
uint8_t major; //!< DCMI Specification Conformance - major ver
uint8_t minor; //!< DCMI Specification Conformance - minor ver
uint8_t paramRevision; //!< Parameter Revision = 02h
@@ -406,7 +365,6 @@ using DCMICaps = std::map<DCMICapParameters, DCMICapEntry>;
*/
struct GetTempReadingsRequest
{
- uint8_t groupID; //!< Group extension identification.
uint8_t sensorType; //!< Type of the sensor
uint8_t entityId; //!< Entity ID
uint8_t entityInstance; //!< Entity Instance (0 means all instances)
@@ -419,7 +377,6 @@ struct GetTempReadingsRequest
*/
struct GetTempReadingsResponseHdr
{
- uint8_t groupID; //!< Group extension identification.
uint8_t numInstances; //!< No. of instances for requested id
uint8_t numDataSets; //!< No. of sets of temperature data
} __attribute__((packed));
@@ -526,7 +483,6 @@ int64_t getPowerReading(sdbusplus::bus::bus& bus);
*/
struct GetPowerReadingRequest
{
- uint8_t groupID; //!< Group extension identification.
uint8_t mode; //!< Mode
uint8_t modeAttribute; //!< Mode Attributes
} __attribute__((packed));
@@ -538,7 +494,6 @@ struct GetPowerReadingRequest
*/
struct GetPowerReadingResponse
{
- uint8_t groupID; //!< Group extension identification.
uint16_t currentPower; //!< Current power in watts
uint16_t minimumPower; //!< Minimum power over sampling duration
//!< in watts
@@ -558,7 +513,6 @@ struct GetPowerReadingResponse
*/
struct GetSensorInfoRequest
{
- uint8_t groupID; //!< Group extension identification.
uint8_t sensorType; //!< Type of the sensor
uint8_t entityId; //!< Entity ID
uint8_t entityInstance; //!< Entity Instance (0 means all instances)
@@ -571,7 +525,6 @@ struct GetSensorInfoRequest
*/
struct GetSensorInfoResponseHdr
{
- uint8_t groupID; //!< Group extension identification.
uint8_t numInstances; //!< No. of instances for requested id
uint8_t numRecords; //!< No. of record ids in the response
} __attribute__((packed));
@@ -594,23 +547,12 @@ enum class DCMIConfigParameters : uint8_t
*/
struct SetConfParamsRequest
{
- uint8_t groupID; //!< Group extension identification.
uint8_t paramSelect; //!< Parameter selector.
uint8_t setSelect; //!< Set Selector (use 00h for parameters that only
//!< have one set).
uint8_t data[]; //!< Configuration parameter data.
} __attribute__((packed));
-/** @struct SetConfParamsResponse
- *
- * DCMI Set DCMI Configuration Parameters Command response.
- * Refer DCMI specification Version 1.1 Section 6.1.2
- */
-struct SetConfParamsResponse
-{
- uint8_t groupID; //!< Group extension identification.
-} __attribute__((packed));
-
/** @struct GetConfParamsRequest
*
* DCMI Get DCMI Configuration Parameters Command.
@@ -618,7 +560,6 @@ struct SetConfParamsResponse
*/
struct GetConfParamsRequest
{
- uint8_t groupID; //!< Group extension identification.
uint8_t paramSelect; //!< Parameter selector.
uint8_t setSelect; //!< Set Selector. Selects a given set of parameters
//!< under a given Parameter selector value. 00h if
@@ -632,7 +573,6 @@ struct GetConfParamsRequest
*/
struct GetConfParamsResponse
{
- uint8_t groupID; //!< Group extension identification.
uint8_t major; //!< DCMI Spec Conformance - major ver = 01h.
uint8_t minor; //!< DCMI Spec Conformance - minor ver = 05h.
uint8_t paramRevision; //!< Parameter Revision = 01h.
diff --git a/docs/configuration.md b/docs/configuration.md
index 61caaf1..4e833d9 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -1,4 +1,4 @@
-#Device ID Configuration#
+# Device ID Configuration
There is a default dev_id.json file provided by
meta-phosphor/common/recipes-phosphor/ipmi/phosphor-ipmi-host.bb
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 0000000..8ccb3ec
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,122 @@
+# Contributing Guidelines
+
+This document attempts to outline some basic rules to follow when contributing
+to OpenBMC's IPMI stack. It does *not* outline coding style; we follow the
+[OpenBMC C++ style guide](https://github.com/openbmc/docs/blob/master/cpp-style-and-conventions)
+for that.
+
+## Organizing Commits
+
+A good commit does exactly one thing. We prefer many small, atomic commits to
+one large commit which makes many functional changes.
+ - Too large: "convert foo to new API; also fix CVE 1234 in bar"
+ - Too small: "move abc.h to top of include list" and "move xyz.h to bottom of
+ include list"
+ - Just right: "convert foo to new API" and "convert foo from tab to space"
+
+Often, creating small commits this way results in a number of commits which are
+dependent on prior commits; Gerrit handles this situation well, so feel free to
+push commits which are based on your change still in review. However, when
+possible, your commit should stand alone on top of master - "Fix whitespace in
+bar()" does not need to depend on "refactor foo()". Said differently, ensure
+that topics which are not related to each other semantically are also not
+related to each other in Git until they are merged into master.
+
+When pushing a stack of patches (current branch is >1 commits ahead of
+origin/master), these commits will show up with that same relationship in
+gerrit. This means that each patch must be merged in order of that relationship.
+So if one of the patches in the middle needs to be changed, all the patches from
+that point on would need to be pushed to maintain the relationship. This will
+effectively rebase the unchanged patches, which would in turn trigger a new CI
+build. Ideally, changes from the entire patchset could be done all in one go to
+reduce unnecessary rebasing.
+
+When someone makes a comment on your commit in Gerrit, modify that commit and
+send it again to Gerrit. This typically involves `git rebase --interactive` or
+`git commit --amend`, for which there are many guides online. As mentioned in
+the paragraph above, when possible you should make changes to multiple patches
+in the same stack before you push, in order to minimize CI and notification
+churn from the rebase operations.
+
+Commits which include changes that can be tested by a unit test should also
+include a unit test to exercise that change, within the same commit. Unit tests
+should be clearly written - even moreso than production code, unit tests are
+meant primarily to be read by humans - and should test both good and bad
+behaviors. Refer to the
+[testing documentation](https://github.com/openbmc/phosphor-host-ipmid/blob/master/docs/testing.md)
+for help writing tests, as well as best practices.
+
+## Formatting Commit Messages
+
+Your commit message should explain:
+
+ - Concisely, *what* change are you making? e.g. "docs: convert from US to UK
+ spelling" This first line of your commit message is the subject line.
+ - Comprehensively, *why* are you making that change? In some cases, like a
+ small refactor, the why is fairly obvious. But in cases like the inclusion of
+ a new feature, you should explain why the feature is needed.
+ - Concisely, *how* did you test? This comes in the form of a "Tested:" footer
+ in your commit message and is required for all code changes in the IPMI
+ stack. It typically consists of copy-pasted ipmitool requests and responses.
+ When possible, use the high-level ipmitool commands (e.g. "ipmitool sensor
+ read 0x1"). In cases where that's not possible, or when testing edge or error
+ cases, it is acceptable to use "ipmitool raw" - but an explanation of your
+ output is appreciated. If the change can be validated entirely by running
+ unit tests, say so in the "Tested:" tag.
+
+Try to include the component you are changing at the front of your subject line;
+this typically comes in the form of the class, module, handler, or directory you
+are modifying. e.g. "apphandler: refactor foo to new API"
+
+Loosely, we try to follow the 50/72 rule for commit messages - that is, the
+subject line should not exceed 50 characters and the body should not exceed 72
+characters. This is common practice in many projects which use Git.
+
+All commit messages must include a Signed-off-by line, which indicates that you
+the contributor have agreed to the Developer Certificate of Origin. This line
+must include the name you commonly use, often a given name and a family name or
+surname. (ok: A. U. Thor, Sam Samuelsson, robert a. heinlein; not ok:
+xXthorXx, Sam, RAH)
+
+## Sending Patches
+
+Like most projects in OpenBMC, we use Gerrit to review patches. Please check
+the MAINTAINERS file to determine who needs to approve your review in order for
+your change to be merged. Submitters will need to manually add their reviewers
+in Gerrit; reviewers are not currently added automatically. Maintainers may not
+see the commit if they have not been added to the review!
+
+## Pace of Review
+
+Contributors who are used to code reviews by their team internal to their own
+company, or who are not used to code reviews at all, are sometimes surprised by
+the pace of code reviews in open source projects. Try to keep in mind that those
+reviewing your patch may be contributing to OpenBMC in a volunteer or
+partial-time capacity, may be in a timezone far removed from your own, and may
+have very deep review queues already of patches which have been waiting longer
+than yours.
+
+If you feel your patch has been missed entirely, of course it's
+alright to email the maintainers (addresses available in MAINTAINERS file) - but
+a reasonable timeframe to do so is on the order of a week, not on the order of
+hours.
+
+Additionally, the IPMI stack has a set of policies for when and how changes can
+be approved; please check the MAINTAINERS file for the full list ("Change
+approval rules").
+
+The maintainers' job is to ensure that incoming patches are as correct and easy
+to maintain as possible. Part of the nature of open source is attrition -
+contributors can come and go easily - so maintainers tend not to put stock in
+promises such as "I will add unit tests in a later patch" or "I will be
+implementing this proposal by the end of next month." This often manifests as
+reviews which may seem harsh or exacting; please keep in mind that the community
+is trying to collaborate with you to build a patch that will benefit the project
+on its own.
+
+## Code of Conduct
+
+We enthusiastically adhere to the same
+[Code of Conduct](https://github.com/openbmc/docs/blob/master/code-of-conduct.md)
+as the rest of OpenBMC. If you have any concerns, please check that document for
+guidelines on who can help you resolve them.
diff --git a/docs/testing.md b/docs/testing.md
new file mode 100644
index 0000000..f5ed04b
--- /dev/null
+++ b/docs/testing.md
@@ -0,0 +1,471 @@
+# Running Tests
+
+## Setting Up Your Environment
+
+For the purposes of this tutorial, we'll be setting up an environment in Docker.
+Docker is handy because it's fairly portable, and won't interfere with the rest
+of your machine setup. It also offers a strong guarantee that you're testing
+the same way that others working on the project are. Finally, we can get away
+with using the same Docker image that's run by the OpenBMC continuous
+integration bot, so we have even more confidence that we're running relevant
+tests the way they'd be run upstream.
+
+### Install Docker
+
+Installation of Docker CE (Community Edition) varies by platform, and may differ
+in your organization. But the
+[Docker Docs](https://docs.docker.com/v17.12/install) are a good place to
+start looking.
+
+Check that the installation was successful by running `sudo docker run
+hello-world`.
+
+### Download OpenBMC Continuous Integration Image
+
+You'll want a couple of different repositories, so start by making a place for
+it all to go, and clone the CI scripts:
+
+```shell
+mkdir openbmc-ci-tests
+cd openbmc-ci-tests
+git clone https://github.com/openbmc/openbmc-build-scripts.git
+```
+
+### Add `phosphor-host-ipmid`
+
+We also need to put a copy of the project you want to test against here. But
+you've probably got a copy checked out already, so we're going to make a
+*git worktree*. You can read more about those by running `git help worktree`,
+but the basic idea is that it's like having a second copy of your repo - but the
+second copy is in sync with your main copy, knows about your local branches, and
+protects you from checking out the same branch in two places.
+
+Your new worktree doesn't know about any untracked files you have in your main
+worktree, so you should get in the habit of committing everything you want to
+run tests against. However, because of the implementation of
+`run-unit-test-docker.sh`, you can't run the CI with untracked changes anyways,
+so this isn't the worst thing in the world. (If you make untracked changes in
+your testing worktree, it's easy to update a commit with those.)
+
+Note the placeholders in the following steps; modify the commands to match your
+directory layout.
+
+```shell
+cd /my/dir/for/phosphor-host-ipmid
+git worktree add /path/to/openbmc-ci-tests/phosphor-host-ipmid
+```
+
+Now, if you `cd /path/to/openbmc-ci-tests`, you should see a directory
+`phosphor-host-ipmid/`, and if you enter it and run `git status` you will see
+that you're likely on a new branch named `phosphor-host-ipmid`. This is just for
+convenience, since you can't check out a branch in your worktree that's already
+checked out somewhere else; you can safely ignore or delete that branch later.
+
+However, Git won't be able to figure out how to get to your main worktree
+(`/my/dir/for/phosphor-host-ipmid`), so we'll need to mount it when we run. Open
+up `/path/to/openbmc-ci-tests/openbmc-build-scripts/run-unit-test-docker.sh` and
+find where we call `docker run`, way down at the bottom. Add an additional
+argument, remembering to escape the newline ('\'):
+
+```shell
+PHOSPHOR_IPMI_HOST_PATH="/my/dir/for/phosphor-host-ipmid"
+
+docker run --blah-blah-existing-flags \
+ -v ${PHOSPHOR_IPMI_HOST_PATH}:${PHOSPHOR_IPMI_HOST_PATH} \
+ -other \
+ -args
+```
+
+Then commit this, so you can make sure not to lose it if you update the scripts
+repo:
+
+```shell
+cd openbmc-build-scripts
+git add run-unit-test-docker.sh
+git commit -m "mount phosphor-host-ipmid"
+```
+
+NOTE: There are other ways to do this besides a worktree; other approaches
+trade the cruft of mounting extra paths to the Docker container for different
+cruft:
+
+You can create a local upstream:
+```shell
+cd openbmc-ci-tests
+mkdir phosphor-host-ipmid
+cd phosphor-host-ipmid
+git init
+cd /my/dir/for/phosphor-host-ipmid
+git remote add /path/to/openbmc-ci-tests/phosphor-host-ipmid ci
+git push ci
+```
+This method would require you to push your topic branch to `ci` and then `git
+checkout` the appropriate branch every time you switched topics:
+```shell
+cd /my/dir/for/phosphor-host-ipmid
+git commit -m "my changes to be tested"
+git push ci
+cd /path/to/openbmc-ci-tests/phosphor-host-ipmid
+git checkout topic-branch
+```
+
+You can also create a symlink from your Git workspace into `openbmc-ci-tests/`.
+This is especially not recommended, since you won't be able to work on your code
+in parallel while the tests run, and since the CI scripts are unhappy when you
+have untracked changes - which you're likely to have during active development.
+
+## Building and Running
+
+The OpenBMC CI scripts take care of the build for you, and run the test suite.
+Build and run like so:
+
+```shell
+sudo WORKSPACE=$(pwd) UNIT_TEST_PKG=phosphor-host-ipmid \
+ ./openbmc-build-scripts/run-unit-test-docker.sh
+```
+
+The first run will take a long time! But afterwards it shouldn't be so bad, as
+many parts of the Docker container are already downloaded and configured.
+
+## Reading Output
+
+Your results will appear in
+`openbmc-ci-tests/phosphor-host-ipmid/test/test-suite.log`, as well as being
+printed to `stdout`. You will also see other `.log` files generated for each
+test file, for example `sample_unittest.log`. All these `*.log` files are
+human-readable and can be examined to determine why something failed
+
+# Writing Tests
+
+Now that you've got an easy working environment for running tests, let's write
+some new ones.
+
+## Setting Up Your Environment
+
+In `/my/dir/for/phosphor-host-ipmid`, create a new branch based on `master` (or
+your current patchset). For this tutorial, let's call it `sensorhandler-tests`.
+
+```shell
+git checkout -b sensorhandler-tests master
+```
+
+This creates a new branch `sensorhandler-tests` which is based on `master`, then
+checks out that branch for you to start hacking.
+
+## Write Some Tests
+
+For this tutorial, we'll be adding some basic unit testing of the struct
+accessors for `get_sdr::GetSdrReq`, just because they're fairly simple. The text
+of the struct and accessors is recreated here:
+
+```c++
+/**
+ * Get SDR
+ */
+namespace get_sdr
+{
+
+struct GetSdrReq
+{
+ uint8_t reservation_id_lsb;
+ uint8_t reservation_id_msb;
+ uint8_t record_id_lsb;
+ uint8_t record_id_msb;
+ uint8_t offset;
+ uint8_t bytes_to_read;
+} __attribute__((packed));
+
+namespace request
+{
+
+inline uint8_t get_reservation_id(GetSdrReq* req)
+{
+ return (req->reservation_id_lsb + (req->reservation_id_msb << 8));
+};
+
+inline uint16_t get_record_id(GetSdrReq* req)
+{
+ return (req->record_id_lsb + (req->record_id_msb << 8));
+};
+
+} // namespace request
+
+...
+} // namespace get_sdr
+```
+
+We'll create the tests in `test/sensorhandler_unittest.cpp`; go ahead and start
+that file with your editor.
+
+First, include the header you want to test, as well as the GTest header:
+
+```c++
+#include <sensorhandler.hpp>
+
+#include <gtest/gtest.h>
+```
+
+Let's plan the test cases we care about before we build any additional
+scaffolding. We've only got two functions - `get_reservation_id()` and
+`get_record_id()`. We want to test:
+
+- "Happy path" - in an ideal case, everything works correctly
+- Error handling - when given bad input, things break as expected
+- Edge cases - when given extremes (e.g. very large or very small numbers),
+ things work correctly or break as expected
+
+For `get_reservation_id()`:
+
+```c++
+TEST(SensorHandlerTest, GetSdrReq_get_reservation_id_HappyPath)
+{
+}
+
+TEST(SensorHandlerTest, GetSdrReq_get_reservation_id_NullInputDies)
+{
+}
+
+TEST(SensorHandlerTest, GetSdrReq_get_reservation_id_Uint16MaxWorksCorrectly)
+{
+}
+```
+
+For `get_record_id()`, we have pretty much the same set of tests:
+
+```c++
+TEST(SensorHandlerTest, GetSdrReq_get_record_id_HappyPath)
+{
+}
+
+TEST(SensorHandlerTest, GetSdrReq_get_record_id_NullInputDies)
+{
+}
+
+TEST(SensorHandlerTest, GetSdrReq_get_record_id_Uint16MaxWorksCorrectly)
+{
+}
+```
+
+In the case of these two methods, there's really not much else to test. Some
+types of edge cases - like overflow/underflow - are prevented by C++'s strong
+typing; other types - like passing the incorrect type - are impossible to
+insulate against because it's possible to cast anything to a `GetSdrReq*` if we
+want. Since these are particularly boring, they make a good example for a
+tutorial like this; in practice, tests you write will likely be for more
+complicated code! We'll talk more about this in the Best Practices section
+below.
+
+Let's implement the `get_reservation_id()` items first. The implementations for
+`get_record_id()` will be identical, so we won't cover them here.
+
+For the happy path, we want to make it very clear that the test value we're
+using is within range, so we express it in binary. We also want to be able to
+ensure that the MSB and LSB are being combined in the correct order, so we make
+sure that the MSB and LSB values are different (don't use `0x3333` as the
+expected ID here). Finally, we want it to be obvious to the reader if we have
+populated the `GetSdrReq` incorrectly, so we've labeled all the fields. Since we
+are only testing one operation, it's okay to use either `ASSERT_EQ` or
+`EXPECT_EQ`; more on that in the Best Practices section.
+
+```c++
+TEST(SensorHandlerTest, GetSdrReq_get_reservation_id_HappyPath)
+{
+ uint16_t expected_id = 0x1234; // Expected ID spans both bytes.
+ GetSdrReq input = {0x34, // Reservation ID LSB
+ 0x12, // Reservation ID MSB
+ 0x00, // Record ID LSB
+ 0x00, // Record ID MSB
+ 0x00, // Offset
+ 0x00}; // Bytes to Read
+
+ uint16_t actual = get_sdr::request::get_reservation_id(&input);
+ ASSERT_EQ(actual, expected_id);
+}
+```
+
+We don't expect that our `GetSdrReq` pointer will ever be null; in this case,
+the null pointer validation is done much, much earlier. So it's okay for us to
+specify that in the unlikely case we're given a null pointer, we die. We don't
+really care what the output message is.
+
+```c++
+TEST(SensorHandlerTest, GetSdrReq_get_reservation_id_NullInputDies)
+{
+ ASSERT_DEATH(get_sdr::request::get_reservation_id(nullptr), ".*");
+}
+```
+
+Finally, while negative values are taken care of by C++'s type system, we can at
+least check that our code still works happily with `UINT16_MAX`. This test is
+similar to the happy path test, but uses an edge value instead.
+
+```c++
+TEST(SensorHandlerTest, GetSdrReq_get_reservation_id_Uint16MaxWorksCorrectly)
+{
+ uint16_t expected_id = 0xFFFF; // Expected ID spans both bytes.
+ GetSdrReq input = {0xFF, // Reservation ID LSB
+ 0xFF, // Reservation ID MSB
+ 0x00, // Record ID LSB
+ 0x00, // Record ID MSB
+ 0x00, // Offset
+ 0x00}; // Bytes to Read
+
+ uint16_t actual = get_sdr::request::get_reservation_id(&input);
+ ASSERT_EQ(actual, expected_id);
+}
+```
+
+The `get_record_id()` tests are identical, except that they are testing the
+Record ID field. They will not be duplicated here.
+
+Finally, we'll need to add the new tests to `test/Makefile.am`. You can mimic other
+existing test setups:
+
+```make
+# Build/add sensorhandler_unittest to test suite
+sensorhandler_unittest_CPPFLAGS = \
+ -Igtest \
+ $(GTEST_CPPFLAGS) \
+ $(AM_CPPFLAGS)
+sensorhandler_unittest_CXXFLAGS = \
+ $(PTHREAD_CFLAGS) \
+ $(CODE_COVERAGE_CXXFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
+ -DBOOST_COROUTINES_NO_DEPRECATION_WARNING
+sensorhandler_unittest_LDFLAGS = \
+ -lgtest_main \
+ -lgtest \
+ -pthread \
+ $(OESDK_TESTCASE_FLAGS) \
+ $(CODE_COVERAGE_LDFLAGS)
+sensorhandler_unittest_SOURCES = \
+ %reldir%/sensorhandler_unittest.cpp
+check_PROGRAMS += %reldir%/sensorhandler_unittest
+```
+
+## Run the New Tests
+
+Commit your test changes. Then, you'll want to checkout the
+`sensorhandler-tests` branch in your CI worktree, which will involve releasing
+it from your main worktree:
+
+```shell
+cd /my/dir/for/phosphor-host-ipmid
+git add test/sensorhandler_unittest.cpp
+git commit -s
+git checkout master # Here you can use any branch except sensorhandler-tests
+cd /path/to/openbmc-ci-tests/phosphor-host-ipmid
+git checkout sensorhandler-tests
+```
+
+Now you can run the test suite as described earlier in the document. If you see
+a linter error when you run, you can actually apply the cleaned-up code easily:
+
+```shell
+cd ./phosphor-host-ipmid
+git diff # Examine the proposed changes
+git add -u # Apply the proposed changes
+git commit --amend
+```
+
+(If you will need to apply the proposed changes to multiple commits, you can do
+this with interactive rebase, which won't be described here.)
+
+## Best Practices
+
+While a good unit test can ensure your code's stability, a flaky or
+poorly-written unit test can make life harder for contributors down the road.
+Some things to remember:
+
+Include both positive (happy-path) and negative (error) testing in your
+testbench. It's not enough to know that the code works when it's supposed to; we
+also need to know that it fails gracefully when something goes wrong. Applying
+edge-case testing helps us find bugs that may take years to occur (and
+reproduce!) in the field.
+
+Keep your tests small. Avoid branching - if you feel a desire to, instead
+explore that codepath in another test. The best tests are easy to read and
+understand.
+
+When a test fails, it's useful if the test is named in such a way that you can
+tell _what it's supposed to do_ and _when_. That way you can be certain whether
+the change you made really broke it or not. A good pattern is
+`Object_NameCircumstanceResult` - for example,
+`FooFactory_OutOfBoundsNameThrowsException`. From the name, it's very clear that
+when some "name" is out of bounds, an exception should be thrown. (What "name"
+is should be clear from the context of the function in `FooFactory`.)
+
+Don't test other people's code. Make sure to limit the test assertions to the
+code under test. For example, don't test what happens if you give a bad input to
+`sdbusplus` when you're supposed to be testing `phosphor-host-ipmid`.
+
+However, don't trust other people's code, either! Try to test _how you respond_
+when a service fails unexpectedly. Rather than checking if `sdbusplus` fails on
+bad inputs, check whether you handle an `sdbusplus` failure gracefully. You can
+use GMock for this kind of testing.
+
+Think about testing when you write your business logic code. Concepts like
+dependency injection and small functions make your code more testable - you'll
+be thanking yourself later when you're writing tests.
+
+Finally, you're very likely to find bugs while writing tests, especially if it's
+for code that wasn't previously unit-tested. It's okay to check in a bugfix
+along with a test that verifies the fix worked, if you're only doing one test
+and one bugfix. If you're checking in a large suite of tests, do the bugfixes in
+independent commits which your test suite commit is based on:
+
+master -> fix Foo.Abc() -> fix Foo.Def() -> Fix Foo.Ghi() -> test Foo class
+
+## Sending for Review
+
+You can send your test update and any associated bugfixes for review to Gerrit
+as you would send any other change. For the `Tested:` field in the commit
+message, you can state that you ran the new unit tests written.
+
+# Reviewing Tests
+
+Tests are written primarily to be read. So when you review a test, you're the
+first customer of that test!
+
+## Best Practices
+
+First, all the best practices listed above for writing tests are things you
+should check for and encourage when you're reading tests.
+
+Next, you should ensure that you can tell what's going on when you read the
+test. If it's not clear to you, it's not going to be clear to someone else, and
+the test is more prone to error - ask!
+
+Finally, think about what's _not_ being tested. If there's a case you're curious
+about and it isn't covered, you should mention it to the committer.
+
+## Quickly Running At Home
+
+Now that you've got a handy setup as described earlier in this document, you can
+quickly download and run the tests yourself. Within the Gerrit change, you
+should be able to find a button that says "Download", which will give you
+commands for various types of downloads into an existing Git repo. Use
+"Checkout", for example:
+
+```shell
+cd openbmc-ci-tests/phosphor-host-ipmid
+git fetch "https://gerrit.openbmc-project.xyz/openbmc/phosphor-host-ipmid" \
+ refs/changes/43/23043/1 && git checkout FETCH_HEAD
+```
+
+This won't disturb the rest of your Git repo state, and will put your CI
+worktree into detached-HEAD mode pointing to the commit that's under review. You
+can then run your tests normally, and even make changes and push again if the
+commit was abandoned or otherwise left to rot by its author.
+
+Doing so can be handy in a number of scenarios:
+
+- Jenkins isn't responding
+- The Jenkins build is broken for a reason beyond the committer's control
+- The committer doesn't have "Ok-To-Test" permission, and you don't have
+ permission to grant it to them
+
+# Credits
+
+Thanks very much to Patrick Venture for his prior work putting together
+documentation on this topic internal to Google.
diff --git a/entity_map_json.cpp b/entity_map_json.cpp
new file mode 100644
index 0000000..7b4e0bf
--- /dev/null
+++ b/entity_map_json.cpp
@@ -0,0 +1,119 @@
+#include "entity_map_json.hpp"
+
+#include <exception>
+#include <fstream>
+#include <ipmid/types.hpp>
+#include <memory>
+#include <nlohmann/json.hpp>
+#include <string>
+#include <utility>
+
+namespace ipmi
+{
+namespace sensor
+{
+
+EntityInfoMapContainer* EntityInfoMapContainer::getContainer()
+{
+ static std::unique_ptr<EntityInfoMapContainer> instance;
+
+ if (!instance)
+ {
+ /* TODO: With multi-threading this would all need to be locked so
+ * the first thread to hit it would set it up.
+ */
+ EntityInfoMap builtEntityMap = buildEntityMapFromFile();
+ instance = std::unique_ptr<EntityInfoMapContainer>(
+ new EntityInfoMapContainer(builtEntityMap));
+ }
+
+ return instance.get();
+}
+
+const EntityInfoMap& EntityInfoMapContainer::getIpmiEntityRecords()
+{
+ return entityRecords;
+}
+
+EntityInfoMap buildEntityMapFromFile()
+{
+ const char* entityMapJsonFilename =
+ "/usr/share/ipmi-providers/entity-map.json";
+ EntityInfoMap builtMap;
+
+ std::ifstream mapFile(entityMapJsonFilename);
+ if (!mapFile.is_open())
+ {
+ return builtMap;
+ }
+
+ auto data = nlohmann::json::parse(mapFile, nullptr, false);
+ if (data.is_discarded())
+ {
+ return builtMap;
+ }
+
+ return buildJsonEntityMap(data);
+}
+
+EntityInfoMap buildJsonEntityMap(const nlohmann::json& data)
+{
+ EntityInfoMap builtMap;
+
+ if (data.type() != nlohmann::json::value_t::array)
+ {
+ return builtMap;
+ }
+
+ try
+ {
+ for (const auto& entry : data)
+ {
+ /* It's an array entry with the following fields: id,
+ * containerEntityId, containerEntityInstance, isList, isLinked,
+ * entities[4]
+ */
+ EntityInfo obj;
+ Id recordId = entry.at("id").get<Id>();
+ obj.containerEntityId =
+ entry.at("containerEntityId").get<uint8_t>();
+ obj.containerEntityInstance =
+ entry.at("containerEntityInstance").get<uint8_t>();
+ obj.isList = entry.at("isList").get<bool>();
+ obj.isLinked = entry.at("isLinked").get<bool>();
+
+ auto jsonEntities = entry.at("entities");
+
+ if (jsonEntities.type() != nlohmann::json::value_t::array)
+ {
+ throw std::runtime_error(
+ "Invalid type for entities entry, must be array");
+ }
+ if (jsonEntities.size() != obj.containedEntities.size())
+ {
+ throw std::runtime_error(
+ "Entities must be in pairs of " +
+ std::to_string(obj.containedEntities.size()));
+ }
+
+ for (std::size_t i = 0; i < obj.containedEntities.size(); i++)
+ {
+ obj.containedEntities[i] = std::make_pair(
+ jsonEntities[i].at("id").get<uint8_t>(),
+ jsonEntities[i].at("instance").get<uint8_t>());
+ }
+
+ builtMap.insert({recordId, obj});
+ }
+ }
+ catch (const std::exception& e)
+ {
+ /* If any entry is invalid, the entire file cannot be trusted. */
+ builtMap.clear();
+ }
+
+ return builtMap;
+}
+
+} // namespace sensor
+} // namespace ipmi
diff --git a/entity_map_json.hpp b/entity_map_json.hpp
new file mode 100644
index 0000000..3ab4ff2
--- /dev/null
+++ b/entity_map_json.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <ipmid/types.hpp>
+#include <memory>
+#include <nlohmann/json.hpp>
+
+namespace ipmi
+{
+namespace sensor
+{
+
+/**
+ * @brief Grab a handle to the entity map.
+ */
+const EntityInfoMap& getIpmiEntityRecords();
+
+/**
+ * @brief Open the default entity map json file, and if present and valid json,
+ * return a built entity map.
+ *
+ * @return the map
+ */
+EntityInfoMap buildEntityMapFromFile();
+
+/**
+ * @brief Given json data validate the data matches the expected format for the
+ * entity map configuration and parse the data into a map of the entities.
+ *
+ * If any entry is invalid, the entire contents passed in is disregarded as
+ * possibly corrupt.
+ *
+ * @param[in] data - the json data
+ * @return the map
+ */
+EntityInfoMap buildJsonEntityMap(const nlohmann::json& data);
+
+/**
+ * @brief Owner of the EntityInfoMap.
+ */
+class EntityInfoMapContainer
+{
+ public:
+ /** Get ahold of the owner. */
+ static EntityInfoMapContainer* getContainer();
+ /** Get ahold of the records. */
+ const EntityInfoMap& getIpmiEntityRecords();
+
+ private:
+ EntityInfoMapContainer(const EntityInfoMap& entityRecords) :
+ entityRecords(entityRecords)
+ {
+ }
+ EntityInfoMap entityRecords;
+};
+
+} // namespace sensor
+} // namespace ipmi
diff --git a/globalhandler.cpp b/globalhandler.cpp
index 27409c2..c192cbb 100644
--- a/globalhandler.cpp
+++ b/globalhandler.cpp
@@ -32,9 +32,11 @@ void resetBMC()
convertForMessage(BMC::Transition::Reboot));
}
-ipmi_ret_t ipmi_global_reset(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request, ipmi_response_t response,
- ipmi_data_len_t data_len, ipmi_context_t context)
+/** @brief implements cold and warm reset commands
+ * @param - None
+ * @returns IPMI completion code.
+ */
+ipmi::RspType<> ipmiGlobalReset()
{
try
{
@@ -43,24 +45,19 @@ ipmi_ret_t ipmi_global_reset(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
catch (std::exception& e)
{
log<level::ERR>(e.what());
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
// Status code.
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = 0;
- return rc;
+ return ipmi::responseSuccess();
}
void register_netfn_global_functions()
{
- // Cold Reset
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_COLD_RESET, NULL,
- ipmi_global_reset, PRIVILEGE_ADMIN);
-
- // <Warm Reset>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_WARM_RESET, NULL,
- ipmi_global_reset, PRIVILEGE_ADMIN);
+ // Cold Reset
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdColdReset, ipmi::Privilege::Admin,
+ ipmiGlobalReset);
return;
}
diff --git a/globalhandler.hpp b/globalhandler.hpp
index 23d3b3e..078b170 100644
--- a/globalhandler.hpp
+++ b/globalhandler.hpp
@@ -6,5 +6,4 @@
enum ipmi_global_control_cmds : uint8_t
{
IPMI_CMD_COLD_RESET = 0x02,
- IPMI_CMD_WARM_RESET = 0x03,
};
diff --git a/host-cmd-manager.cpp b/host-cmd-manager.cpp
index 9efb787..f3aba7f 100644
--- a/host-cmd-manager.cpp
+++ b/host-cmd-manager.cpp
@@ -33,7 +33,6 @@ using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
namespace sdbusRule = sdbusplus::bus::match::rules;
-namespace variant_ns = sdbusplus::message::variant_ns;
Manager::Manager(sdbusplus::bus::bus& bus) :
bus(bus), timer(std::bind(&Manager::hostTimeout, this)),
@@ -183,7 +182,7 @@ void Manager::clearQueueOnPowerOn(sdbusplus::message::message& msg)
}
auto& requestedState =
- variant_ns::get<std::string>(properties.at(HOST_TRANS_PROP));
+ std::get<std::string>(properties.at(HOST_TRANS_PROP));
if (server::Host::convertTransitionFromString(requestedState) ==
server::Host::Transition::On)
diff --git a/host-interface.cpp b/host-interface.cpp
index 69ab376..1f9bfcb 100644
--- a/host-interface.cpp
+++ b/host-interface.cpp
@@ -54,7 +54,7 @@ void Host::execute(Base::Host::Command command)
std::placeholders::_1,
std::placeholders::_2));
- return ipmid_send_cmd_to_host(std::move(cmd));
+ ipmid_send_cmd_to_host(std::move(cmd));
}
// Called into by Command Manager
diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf
index 961214d..5397115 100644
--- a/host-ipmid-whitelist.conf
+++ b/host-ipmid-whitelist.conf
@@ -26,7 +26,9 @@
0x06:0x36 //<App>:<Get BT Interface Capabilities>
0x06:0x37 //<App>:<Get System GUID>
0x06:0x42 //<App>:<Get Channel Info Command>
+0x06:0x4D //<App>:<Get User Payload Access>
0x06:0x4E //<App>:<Get Channel Payload Support>
+0x06:0x4F //<App>:<Get Channel Payload Version>
0x06:0x54 //<App>:<Get Channel Cipher Suites>
0x0A:0x10 //<Storage>:<Get FRU Inventory Area Info>
0x0A:0x11 //<Storage>:<Read FRU Data>
diff --git a/include/Makefile.am b/include/Makefile.am
index b7c303a..08824c4 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,6 +1,8 @@
nobase_include_HEADERS = \
ipmid/api.hpp \
ipmid/api-types.hpp \
+ ipmid/sessiondef.hpp \
+ ipmid/sessionhelper.hpp \
ipmid/filter.hpp \
ipmid/handler.hpp \
ipmid/message.hpp \
diff --git a/include/ipmid/api.hpp b/include/ipmid/api.hpp
index f4cbf13..6e33470 100644
--- a/include/ipmid/api.hpp
+++ b/include/ipmid/api.hpp
@@ -47,7 +47,7 @@ std::shared_ptr<sdbusplus::asio::connection> getSdBus();
template <typename WorkFn>
static inline void post_work(WorkFn work)
{
- getIoContext()->post(std::forward<WorkFn>(work));
+ boost::asio::post(*getIoContext(), std::forward<WorkFn>(work));
}
enum class SignalResponse : int
diff --git a/include/ipmid/handler.hpp b/include/ipmid/handler.hpp
index 1421c3d..b6c6c0f 100644
--- a/include/ipmid/handler.hpp
+++ b/include/ipmid/handler.hpp
@@ -153,6 +153,7 @@ class IpmiHandler final : public HandlerBase
using ResultType = boost::callable_traits::return_type_t<Handler>;
UnpackArgsType unpackArgs;
+ request->payload.trailingOk = false;
ipmi::Cc unpackError = request->unpack(unpackArgs);
if (unpackError != ipmi::ccSuccess)
{
@@ -178,45 +179,47 @@ class IpmiHandler final : public HandlerBase
* parameter selector. All the remaining data can be extracted using
* the Payload class and the unpack API available to the Payload class.
*/
- std::optional<InputArgsType> inputArgs;
- if constexpr (std::tuple_size<InputArgsType>::value > 0)
+ ResultType result;
+ try
{
- if constexpr (std::is_same<std::tuple_element_t<0, InputArgsType>,
- boost::asio::yield_context>::value)
- {
- inputArgs.emplace(std::tuple_cat(
- std::forward_as_tuple(*(request->ctx->yield)),
- std::move(unpackArgs)));
- }
- else if constexpr (std::is_same<
- std::tuple_element_t<0, InputArgsType>,
- ipmi::Context::ptr>::value)
- {
- inputArgs.emplace(
- std::tuple_cat(std::forward_as_tuple(request->ctx),
- std::move(unpackArgs)));
- }
- else if constexpr (std::is_same<
- std::tuple_element_t<0, InputArgsType>,
- ipmi::message::Request::ptr>::value)
+ std::optional<InputArgsType> inputArgs;
+ if constexpr (std::tuple_size<InputArgsType>::value > 0)
{
- inputArgs.emplace(std::tuple_cat(std::forward_as_tuple(request),
- std::move(unpackArgs)));
+ if constexpr (std::is_same<
+ std::tuple_element_t<0, InputArgsType>,
+ boost::asio::yield_context>::value)
+ {
+ inputArgs.emplace(std::tuple_cat(
+ std::forward_as_tuple(request->ctx->yield),
+ std::move(unpackArgs)));
+ }
+ else if constexpr (std::is_same<
+ std::tuple_element_t<0, InputArgsType>,
+ ipmi::Context::ptr>::value)
+ {
+ inputArgs.emplace(
+ std::tuple_cat(std::forward_as_tuple(request->ctx),
+ std::move(unpackArgs)));
+ }
+ else if constexpr (std::is_same<
+ std::tuple_element_t<0, InputArgsType>,
+ ipmi::message::Request::ptr>::value)
+ {
+ inputArgs.emplace(std::tuple_cat(
+ std::forward_as_tuple(request), std::move(unpackArgs)));
+ }
+ else
+ {
+ // no special parameters were requested (but others were)
+ inputArgs.emplace(std::move(unpackArgs));
+ }
}
else
{
- // no special parameters were requested (but others were)
- inputArgs.emplace(std::move(unpackArgs));
+ // no parameters were requested
+ inputArgs = std::move(unpackArgs);
}
- }
- else
- {
- // no parameters were requested
- inputArgs = std::move(unpackArgs);
- }
- ResultType result;
- try
- {
+
// execute the registered callback function and get the
// ipmi::RspType<>
result = std::apply(handler_, *inputArgs);
@@ -264,6 +267,7 @@ class IpmiHandler final : public HandlerBase
};
#ifdef ALLOW_DEPRECATED_API
+static constexpr size_t maxLegacyBufferSize = 64 * 1024;
/**
* @brief Legacy IPMI handler class
*
@@ -307,17 +311,17 @@ class IpmiHandler<ipmid_callback_t> final : public HandlerBase
executeCallback(message::Request::ptr request) override
{
message::Response::ptr response = request->makeResponse();
- size_t len = request->payload.size();
// allocate a big response buffer here
- response->payload.resize(
- getChannelMaxTransferSize(request->ctx->channel));
+ response->payload.resize(maxLegacyBufferSize);
+ size_t len = request->payload.size() - request->payload.rawIndex;
Cc ccRet{ccSuccess};
try
{
- ccRet = handler_(request->ctx->netFn, request->ctx->cmd,
- request->payload.data(), response->payload.data(),
- &len, handlerCtx);
+ ccRet =
+ handler_(request->ctx->netFn, request->ctx->cmd,
+ request->payload.data() + request->payload.rawIndex,
+ response->payload.data(), &len, handlerCtx);
}
catch (const std::exception& e)
{
@@ -396,16 +400,17 @@ class IpmiHandler<oem::Handler> final : public HandlerBase
executeCallback(message::Request::ptr request) override
{
message::Response::ptr response = request->makeResponse();
- size_t len = request->payload.size();
// allocate a big response buffer here
- response->payload.resize(
- getChannelMaxTransferSize(request->ctx->channel));
+ response->payload.resize(maxLegacyBufferSize);
+ size_t len = request->payload.size() - request->payload.rawIndex;
Cc ccRet{ccSuccess};
try
{
- ccRet = handler_(request->ctx->cmd, request->payload.data(),
- response->payload.data(), &len);
+ ccRet =
+ handler_(request->ctx->cmd,
+ request->payload.data() + request->payload.rawIndex,
+ response->payload.data(), &len);
}
catch (const std::exception& e)
{
diff --git a/include/ipmid/message.hpp b/include/ipmid/message.hpp
index 030618f..c828e3c 100644
--- a/include/ipmid/message.hpp
+++ b/include/ipmid/message.hpp
@@ -18,10 +18,12 @@
#include <algorithm>
#include <boost/asio/spawn.hpp>
#include <cstdint>
+#include <exception>
#include <ipmid/api-types.hpp>
#include <ipmid/message/types.hpp>
#include <memory>
#include <phosphor-logging/log.hpp>
+#include <sdbusplus/asio/connection.hpp>
#include <tuple>
#include <utility>
#include <vector>
@@ -33,23 +35,33 @@ struct Context
{
using ptr = std::shared_ptr<Context>;
- Context() = default;
-
- Context(NetFn netFn, Cmd cmd, int channel, int userId, Privilege priv,
- boost::asio::yield_context* yield = nullptr) :
- netFn(netFn),
- cmd(cmd), channel(channel), userId(userId), priv(priv), yield(yield)
+ Context() = delete;
+ Context(const Context&) = default;
+ Context& operator=(const Context&) = default;
+ Context(Context&&) = delete;
+ Context& operator=(Context&&) = delete;
+
+ Context(std::shared_ptr<sdbusplus::asio::connection> bus, NetFn netFn,
+ Cmd cmd, int channel, int userId, uint32_t sessionId,
+ Privilege priv, int rqSA, boost::asio::yield_context& yield) :
+ bus(bus),
+ netFn(netFn), cmd(cmd), channel(channel), userId(userId),
+ sessionId(sessionId), priv(priv), rqSA(rqSA), yield(yield)
{
}
+ std::shared_ptr<sdbusplus::asio::connection> bus;
// normal IPMI context (what call is this, from whence it came...)
- NetFn netFn = 0;
- Cmd cmd = 0;
- int channel = 0;
- int userId = 0;
- Privilege priv = Privilege::None;
- // if non-null, use this to do blocking asynchronous asio calls
- boost::asio::yield_context* yield = nullptr;
+ NetFn netFn;
+ Cmd cmd;
+ int channel;
+ int userId;
+ uint32_t sessionId;
+ Privilege priv;
+ // srcAddr is only set on IPMB requests because
+ // Platform Event Message needs it to determine the incoming format
+ int rqSA;
+ boost::asio::yield_context yield;
};
namespace message
@@ -99,15 +111,15 @@ struct Payload
Payload(Payload&&) = default;
Payload& operator=(Payload&&) = default;
- explicit Payload(std::vector<uint8_t>&& data) :
- raw(std::move(data)), unpackCheck(false)
+ explicit Payload(std::vector<uint8_t>&& data) : raw(std::move(data))
{
}
~Payload()
{
using namespace phosphor::logging;
- if (trailingOk && !unpackCheck && !fullyUnpacked())
+ if (raw.size() != 0 && std::uncaught_exceptions() == 0 && !trailingOk &&
+ !unpackCheck && !unpackError)
{
log<level::ERR>("Failed to check request for full unpack");
}
@@ -252,6 +264,27 @@ struct Payload
return packRet;
}
+ /**
+ * @brief Prepends another payload to this one
+ *
+ * Avoid using this unless absolutely required since it inserts into the
+ * front of the response payload.
+ *
+ * @param p - The payload to prepend
+ *
+ * @retunr int - non-zero on prepend errors
+ */
+ int prepend(const ipmi::message::Payload& p)
+ {
+ if (bitCount != 0 || p.bitCount != 0)
+ {
+ return 1;
+ }
+ raw.reserve(raw.size() + p.raw.size());
+ raw.insert(raw.begin(), p.raw.begin(), p.raw.end());
+ return 0;
+ }
+
/******************************************************************
* Request operations
*****************************************************************/
@@ -445,8 +478,8 @@ struct Payload
size_t bitCount = 0;
std::vector<uint8_t> raw;
size_t rawIndex = 0;
- bool trailingOk = false;
- bool unpackCheck = true;
+ bool trailingOk = true;
+ bool unpackCheck = false;
bool unpackError = false;
};
@@ -513,6 +546,21 @@ struct Response
return payload.pack(t);
}
+ /**
+ * @brief Prepends another payload to this one
+ *
+ * Avoid using this unless absolutely required since it inserts into the
+ * front of the response payload.
+ *
+ * @param p - The payload to prepend
+ *
+ * @retunr int - non-zero on prepend errors
+ */
+ int prepend(const ipmi::message::Payload& p)
+ {
+ return payload.prepend(p);
+ }
+
Payload payload;
Context::ptr ctx;
Cc cc;
@@ -560,19 +608,20 @@ struct Request
int unpack(Args&&... args)
{
int unpackRet = payload.unpack(std::forward<Args>(args)...);
- if (unpackRet == ipmi::ccSuccess)
+ if (unpackRet != ipmi::ccSuccess)
+ {
+ // not all bits were consumed by requested parameters
+ return ipmi::ccReqDataLenInvalid;
+ }
+ if (!payload.trailingOk)
{
- if (!payload.trailingOk)
+ if (!payload.fullyUnpacked())
{
- if (!payload.fullyUnpacked())
- {
- // not all bits were consumed by requested parameters
- return ipmi::ccReqDataLenInvalid;
- }
- payload.unpackCheck = false;
+ // not all bits were consumed by requested parameters
+ return ipmi::ccReqDataLenInvalid;
}
}
- return unpackRet;
+ return ipmi::ccSuccess;
}
/**
diff --git a/include/ipmid/message/pack.hpp b/include/ipmid/message/pack.hpp
index 18863c4..598e650 100644
--- a/include/ipmid/message/pack.hpp
+++ b/include/ipmid/message/pack.hpp
@@ -20,6 +20,7 @@
#include <memory>
#include <optional>
#include <phosphor-logging/log.hpp>
+#include <string_view>
#include <tuple>
#include <utility>
#include <variant>
@@ -239,6 +240,26 @@ struct PackSingle<std::vector<uint8_t>>
{
static int op(Payload& p, const std::vector<uint8_t>& t)
{
+ if (p.bitCount != 0)
+ {
+ return 1;
+ }
+ p.raw.reserve(p.raw.size() + t.size());
+ p.raw.insert(p.raw.end(), t.begin(), t.end());
+ return 0;
+ }
+};
+
+/** @brief Specialization of PackSingle for std::string_view */
+template <>
+struct PackSingle<std::string_view>
+{
+ static int op(Payload& p, const std::string_view& t)
+ {
+ if (p.bitCount != 0)
+ {
+ return 1;
+ }
p.raw.reserve(p.raw.size() + t.size());
p.raw.insert(p.raw.end(), t.begin(), t.end());
return 0;
@@ -259,6 +280,22 @@ struct PackSingle<std::variant<T...>>
}
};
+/** @brief Specialization of PackSingle for Payload */
+template <>
+struct PackSingle<Payload>
+{
+ static int op(Payload& p, const Payload& t)
+ {
+ if (p.bitCount != 0 || t.bitCount != 0)
+ {
+ return 1;
+ }
+ p.raw.reserve(p.raw.size() + t.raw.size());
+ p.raw.insert(p.raw.end(), t.raw.begin(), t.raw.end());
+ return 0;
+ }
+};
+
} // namespace details
} // namespace message
diff --git a/include/ipmid/message/unpack.hpp b/include/ipmid/message/unpack.hpp
index 94f80f1..d9ccba4 100644
--- a/include/ipmid/message/unpack.hpp
+++ b/include/ipmid/message/unpack.hpp
@@ -99,26 +99,29 @@ struct UnpackSingle
}
return 0;
}
- else
+ else if constexpr (utility::is_tuple<T>::value)
{
- if constexpr (utility::is_tuple<T>::value)
+ bool priorError = p.unpackError;
+ size_t priorIndex = p.rawIndex;
+ // more stuff to unroll if partial bytes are out
+ size_t priorBitCount = p.bitCount;
+ fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
+ int ret = p.unpack(t);
+ if (ret != 0)
{
- bool priorError = p.unpackError;
- size_t priorIndex = p.rawIndex;
- // more stuff to unroll if partial bytes are out
- size_t priorBitCount = p.bitCount;
- fixed_uint_t<details::bitStreamSize> priorBits = p.bitStream;
- int ret = p.unpack(t);
- if (ret != 0)
- {
- t = T();
- p.rawIndex = priorIndex;
- p.bitStream = priorBits;
- p.bitCount = priorBitCount;
- p.unpackError = priorError;
- }
- return 0;
+ t = T();
+ p.rawIndex = priorIndex;
+ p.bitStream = priorBits;
+ p.bitCount = priorBitCount;
+ p.unpackError = priorError;
}
+ return ret;
+ }
+ else
+ {
+ static_assert(
+ utility::dependent_false<T>::value,
+ "Attempt to unpack a type that has no IPMI unpack operation");
}
}
};
@@ -289,18 +292,21 @@ struct UnpackSingle<std::vector<T>>
{
static int op(Payload& p, std::vector<T>& t)
{
- int ret = 0;
while (p.rawIndex < p.raw.size())
{
t.emplace_back();
- ret = UnpackSingle<T>::op(p, t.back());
- if (ret)
+ if (UnpackSingle<T>::op(p, t.back()))
{
t.pop_back();
break;
}
}
- return ret;
+ // unpacking a vector is always successful:
+ // either stuff was unpacked successfully (return 0)
+ // or stuff was not unpacked, but should still return
+ // success because an empty vector or a not-fully-unpacked
+ // payload is not a failure.
+ return 0;
}
};
@@ -324,13 +330,9 @@ struct UnpackSingle<Payload>
{
static int op(Payload& p, Payload& t)
{
+ t = p;
// mark that this payload is being included in the args
p.trailingOk = true;
- t = p;
- // reset the unpacking flags so it can be properly checked
- t.trailingOk = false;
- t.unpackCheck = true;
- t.unpackError = false;
return 0;
}
};
diff --git a/include/ipmid/sessiondef.hpp b/include/ipmid/sessiondef.hpp
new file mode 100644
index 0000000..ac63f8f
--- /dev/null
+++ b/include/ipmid/sessiondef.hpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2019 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#pragma once
+
+namespace session
+{
+
+static constexpr auto sessionManagerRootPath =
+ "/xyz/openbmc_project/ipmi/session";
+static constexpr auto sessionIntf = "xyz.openbmc_project.Ipmi.SessionInfo";
+static constexpr uint8_t ipmi20VerSession = 0x01;
+static constexpr size_t maxSessionCountPerChannel = 15;
+static constexpr size_t sessionZero = 0;
+static constexpr size_t maxSessionlessCount = 1;
+static constexpr uint8_t invalidSessionID = 0;
+static constexpr uint8_t invalidSessionHandle = 0;
+static constexpr uint8_t defaultSessionHandle = 0xFF;
+static constexpr uint8_t maxNetworkInstanceSupported = 4;
+static constexpr uint8_t ccInvalidSessionId = 0x87;
+static constexpr uint8_t ccInvalidSessionHandle = 0x88;
+static constexpr uint8_t searchCurrentSession = 0;
+static constexpr uint8_t searchSessionByHandle = 0xFE;
+static constexpr uint8_t searchSessionById = 0xFF;
+// MSB BIT 7 BIT 6 assigned for netipmid instance in session handle.
+static constexpr uint8_t multiIntfaceSessionHandleMask = 0x3F;
+
+// MSB BIT 31-BIT30 assigned for netipmid instance in session ID
+static constexpr uint32_t multiIntfaceSessionIDMask = 0x3FFFFFFF;
+
+enum class State : uint8_t
+{
+ inactive, // Session is not in use
+ setupInProgress, // Session Setup Sequence is progressing
+ active, // Session is active
+ tearDownInProgress, // When Closing Session
+};
+
+} // namespace session
diff --git a/include/ipmid/sessionhelper.hpp b/include/ipmid/sessionhelper.hpp
new file mode 100644
index 0000000..a96f037
--- /dev/null
+++ b/include/ipmid/sessionhelper.hpp
@@ -0,0 +1,88 @@
+#include <sstream>
+#include <string>
+
+/**
+ * @brief parse session input payload.
+ *
+ * This function retrives the session id and session handle from the session
+ * object path.
+ * A valid object path will be in the form
+ * "/xyz/openbmc_project/ipmi/session/channel/sessionId_sessionHandle"
+ *
+ * Ex: "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"
+ * SessionId : 0X12a4567d
+ * SessionHandle: 0X8a
+
+ * @param[in] objectPath - session object path
+ * @param[in] sessionId - retrived session id will be asigned.
+ * @param[in] sessionHandle - retrived session handle will be asigned.
+ *
+ * @return true if session id and session handle are retrived else returns
+ * false.
+ */
+bool parseCloseSessionInputPayload(const std::string& objectPath,
+ uint32_t& sessionId, uint8_t& sessionHandle)
+{
+ if (objectPath.empty())
+ {
+ return false;
+ }
+ // getting the position of session id and session handle string from
+ // object path.
+ std::size_t ptrPosition = objectPath.rfind("/");
+ uint16_t tempSessionHandle = 0;
+
+ if (ptrPosition != std::string::npos)
+ {
+ // get the sessionid & session handle string from the session object
+ // path Ex: sessionIdString: "12a4567d_8a"
+ std::string sessionIdString = objectPath.substr(ptrPosition + 1);
+ std::size_t pos = sessionIdString.rfind("_");
+
+ if (pos != std::string::npos)
+ {
+ // extracting the session handle
+ std::string sessionHandleString = sessionIdString.substr(pos + 1);
+ // extracting the session id
+ sessionIdString = sessionIdString.substr(0, pos);
+ // converting session id string and session handle string to
+ // hexadecimal.
+ std::stringstream handle(sessionHandleString);
+ handle >> std::hex >> tempSessionHandle;
+ sessionHandle = tempSessionHandle & 0xFF;
+ std::stringstream idString(sessionIdString);
+ idString >> std::hex >> sessionId;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * @brief is session object matched.
+ *
+ * This function checks whether the objectPath contains reqSessionId and
+ * reqSessionHandle, e.g., "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"
+ * matches sessionId 0x12a4567d and sessionHandle 0x8a.
+ *
+ * @param[in] objectPath - session object path
+ * @param[in] reqSessionId - request session id
+ * @param[in] reqSessionHandle - request session handle
+ *
+ * @return true if the object is matched else return false
+ **/
+bool isSessionObjectMatched(const std::string objectPath,
+ const uint32_t reqSessionId,
+ const uint8_t reqSessionHandle)
+{
+ uint32_t sessionId = 0;
+ uint8_t sessionHandle = 0;
+
+ if (parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle))
+ {
+ return (reqSessionId == sessionId) ||
+ (reqSessionHandle == sessionHandle);
+ }
+
+ return false;
+}
diff --git a/include/ipmid/types.hpp b/include/ipmid/types.hpp
index 57c5873..3e64cb4 100644
--- a/include/ipmid/types.hpp
+++ b/include/ipmid/types.hpp
@@ -5,6 +5,7 @@
#include <map>
#include <sdbusplus/server.hpp>
#include <string>
+#include <variant>
namespace ipmi
{
@@ -15,9 +16,8 @@ using DbusInterface = std::string;
using DbusObjectInfo = std::pair<DbusObjectPath, DbusService>;
using DbusProperty = std::string;
-using Value = sdbusplus::message::variant<bool, uint8_t, int16_t, uint16_t,
- int32_t, uint32_t, int64_t, uint64_t,
- double, std::string>;
+using Value = std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+ int64_t, uint64_t, double, std::string>;
using PropertyMap = std::map<DbusProperty, Value>;
@@ -100,7 +100,15 @@ struct GetReadingResponse
constexpr auto inventoryRoot = "/xyz/openbmc_project/inventory";
-using GetSensorResponse = std::array<uint8_t, sizeof(GetReadingResponse)>;
+struct GetSensorResponse
+{
+ uint8_t reading; // sensor reading
+ bool readingOrStateUnavailable; // 1 = reading/state unavailable
+ bool scanningEnabled; // 0 = sensor scanning disabled
+ bool allEventMessagesEnabled; // 0 = All Event Messages disabled
+ uint8_t thresholdLevelsStates; // threshold/discrete sensor states
+ uint8_t discreteReadingSensorStates; // discrete-only, optional states
+};
using OffsetValueMap = std::map<Offset, Values>;
@@ -215,12 +223,7 @@ using EntityInfoMap = std::map<Id, EntityInfo>;
namespace network
{
-using ChannelEthMap = std::map<int, std::string>;
-
constexpr auto MAC_ADDRESS_FORMAT = "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx";
-constexpr auto IP_ADDRESS_FORMAT = "%u.%u.%u.%u";
-constexpr auto PREFIX_FORMAT = "%hhd";
-constexpr auto ADDR_TYPE_FORMAT = "%hhx";
constexpr auto IPV4_ADDRESS_SIZE_BYTE = 4;
constexpr auto IPV6_ADDRESS_SIZE_BYTE = 16;
@@ -228,20 +231,6 @@ constexpr auto IPV6_ADDRESS_SIZE_BYTE = 16;
constexpr auto DEFAULT_MAC_ADDRESS = "00:00:00:00:00:00";
constexpr auto DEFAULT_ADDRESS = "0.0.0.0";
-constexpr auto MAC_ADDRESS_SIZE_BYTE = 6;
-constexpr auto VLAN_SIZE_BYTE = 2;
-constexpr auto IPSRC_SIZE_BYTE = 1;
-constexpr auto BITS_32 = 32;
-constexpr auto MASK_32_BIT = 0xFFFFFFFF;
-constexpr auto VLAN_ID_MASK = 0x00000FFF;
-constexpr auto VLAN_ENABLE_MASK = 0x8000;
-
-enum class IPOrigin : uint8_t
-{
- UNSPECIFIED = 0,
- STATIC = 1,
- DHCP = 2,
-};
-
} // namespace network
+
} // namespace ipmi
diff --git a/include/ipmid/utility.hpp b/include/ipmid/utility.hpp
index 79c76f7..0c39e92 100644
--- a/include/ipmid/utility.hpp
+++ b/include/ipmid/utility.hpp
@@ -214,6 +214,13 @@ struct is_tuple<std::tuple<T...>> : std::true_type
{
};
+/** @brief used for static_assert in a constexpr-if else statement
+ */
+template <typename T>
+struct dependent_false : std::false_type
+{
+};
+
} // namespace utility
} // namespace ipmi
diff --git a/include/ipmid/utils.hpp b/include/ipmid/utils.hpp
index 9ef1488..3515eb6 100644
--- a/include/ipmid/utils.hpp
+++ b/include/ipmid/utils.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <chrono>
+#include <ipmid/api-types.hpp>
#include <ipmid/types.hpp>
#include <optional>
#include <sdbusplus/server.hpp>
@@ -112,20 +113,6 @@ DbusObjectInfo getDbusObject(sdbusplus::bus::bus& bus,
const std::string& subtreePath = ROOT,
const std::string& match = {});
-/** @brief Get the ipObject of first dbus IP object of Non-LinkLocalIPAddress
- * type from the given subtree, if not available gets IP object of
- * LinkLocalIPAddress type.
- * @param[in] bus - DBUS Bus Object.
- * @param[in] interface - Dbus interface.
- * @param[in] subtreePath - subtree from where the search should start.
- * @param[in] match - identifier for object.
- * @return On success returns the object having objectpath and servicename.
- */
-DbusObjectInfo getIPObject(sdbusplus::bus::bus& bus,
- const std::string& interface,
- const std::string& subtreePath,
- const std::string& match);
-
/** @brief Gets the value associated with the given object
* and the interface.
* @param[in] bus - DBUS Bus Object.
@@ -250,59 +237,13 @@ void callDbusMethod(sdbusplus::bus::bus& bus, const std::string& service,
} // namespace method_no_args
-namespace network
-{
-
-constexpr auto ROOT = "/xyz/openbmc_project/network";
-constexpr auto SERVICE = "xyz.openbmc_project.Network";
-constexpr auto IP_TYPE = "ipv4";
-constexpr auto IPV4_PREFIX = "169.254";
-constexpr auto IPV6_PREFIX = "fe80";
-constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP";
-constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
-constexpr auto SYSTEMCONFIG_INTERFACE =
- "xyz.openbmc_project.Network.SystemConfiguration";
-constexpr auto ETHERNET_INTERFACE =
- "xyz.openbmc_project.Network.EthernetInterface";
-constexpr auto IP_CREATE_INTERFACE = "xyz.openbmc_project.Network.IP.Create";
-constexpr auto VLAN_CREATE_INTERFACE =
- "xyz.openbmc_project.Network.VLAN.Create";
-constexpr auto VLAN_INTERFACE = "xyz.openbmc_project.Network.VLAN";
-
-/* @brief converts the given subnet into prefix notation.
- * @param[in] addressFamily - IP address family(AF_INET/AF_INET6).
- * @param[in] mask - Subnet Mask.
- * @returns prefix.
+/** @brief Perform the low-level i2c bus write-read.
+ * @param[in] i2cBus - i2c bus device node name, such as /dev/i2c-2.
+ * @param[in] slaveAddr - i2c device slave address.
+ * @param[in] writeData - The data written to i2c device.
+ * @param[out] readBuf - Data read from the i2c device.
*/
-uint8_t toPrefix(int addressFamily, const std::string& subnetMask);
-
-/** @brief Sets the ip on the system.
- * @param[in] bus - DBUS Bus Object.
- * @param[in] service - Dbus service name.
- * @param[in] objPath - Dbus object path.
- * @param[in] protocolType - Protocol type
- * @param[in] ipaddress - IPaddress.
- * @param[in] prefix - Prefix length.
- */
-void createIP(sdbusplus::bus::bus& bus, const std::string& service,
- const std::string& objPath, const std::string& protocolType,
- const std::string& ipaddress, uint8_t prefix);
-
-/** @brief Creates the VLAN on the given interface.
- * @param[in] bus - DBUS Bus Object.
- * @param[in] service - Dbus service name.
- * @param[in] objPath - Dbus object path.
- * @param[in] interface - EthernetInterface.
- * @param[in] vlanID - Vlan ID.
- */
-void createVLAN(sdbusplus::bus::bus& bus, const std::string& service,
- const std::string& objPath, const std::string& interface,
- uint32_t vlanID);
-
-/** @brief Gets the vlan id from the given object path.
- * @param[in] path - Dbus object path.
- */
-uint32_t getVLAN(const std::string& path);
-
-} // namespace network
+ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr,
+ std::vector<uint8_t> writeData,
+ std::vector<uint8_t>& readBuf);
} // namespace ipmi
diff --git a/ipmi_fru_info_area.cpp b/ipmi_fru_info_area.cpp
index 47a8a8e..b220f02 100644
--- a/ipmi_fru_info_area.cpp
+++ b/ipmi_fru_info_area.cpp
@@ -2,9 +2,11 @@
#include <algorithm>
#include <ctime>
+#include <iomanip>
#include <map>
#include <numeric>
#include <phosphor-logging/elog.hpp>
+#include <sstream>
namespace ipmi
{
@@ -13,12 +15,12 @@ namespace fru
using namespace phosphor::logging;
// Property variables
-static constexpr auto partNumber = "PartNumber";
-static constexpr auto serialNumber = "SerialNumber";
+static constexpr auto partNumber = "Part Number";
+static constexpr auto serialNumber = "Serial Number";
static constexpr auto manufacturer = "Manufacturer";
-static constexpr auto buildDate = "BuildDate";
+static constexpr auto buildDate = "Mfg Date";
static constexpr auto model = "Model";
-static constexpr auto prettyName = "PrettyName";
+static constexpr auto prettyName = "Name";
static constexpr auto version = "Version";
static constexpr auto type = "Type";
@@ -193,6 +195,29 @@ void appendData(const Property& key, const PropertyMap& propMap,
}
}
+std::time_t timeStringToRaw(const std::string& input)
+{
+ // TODO: For non-US region timestamps, pass in region information for the
+ // FRU to avoid the month/day swap.
+ // 2017-02-24 - 13:59:00, Tue Nov 20 23:08:00 2018
+ static const std::vector<std::string> patterns = {"%Y-%m-%d - %H:%M:%S",
+ "%a %b %d %H:%M:%S %Y"};
+
+ std::tm time = {};
+
+ for (const auto& pattern : patterns)
+ {
+ std::istringstream timeStream(input);
+ timeStream >> std::get_time(&time, pattern.c_str());
+ if (!timeStream.fail())
+ {
+ break;
+ }
+ }
+
+ return std::mktime(&time);
+}
+
/**
* @brief Appends Build Date
*
@@ -205,9 +230,7 @@ void appendMfgDate(const PropertyMap& propMap, FruAreaData& data)
auto iter = propMap.find(buildDate);
if ((iter != propMap.end()) && (iter->second.size() > 0))
{
- tm time = {};
- strptime(iter->second.c_str(), "%F - %H:%M:%S", &time);
- time_t raw = mktime(&time);
+ std::time_t raw = timeStringToRaw(iter->second);
// From FRU Spec:
// "Mfg. Date / Time
diff --git a/ipmid-new.cpp b/ipmid-new.cpp
index 2a2d4a6..abd0a10 100644
--- a/ipmid-new.cpp
+++ b/ipmid-new.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <any>
+#include <boost/algorithm/string.hpp>
#include <dcmihandler.hpp>
#include <exception>
#include <filesystem>
@@ -232,17 +233,18 @@ message::Response::ptr executeIpmiCommandCommon(
{
// filter the command first; a non-null message::Response::ptr
// means that the message has been rejected for some reason
- message::Response::ptr response = filterIpmiCommand(request);
- if (response)
- {
- return response;
- }
+ message::Response::ptr filterResponse = filterIpmiCommand(request);
Cmd cmd = request->ctx->cmd;
unsigned int key = makeCmdKey(keyCommon, cmd);
auto cmdIter = handlers.find(key);
if (cmdIter != handlers.end())
{
+ // only return the filter response if the command is found
+ if (filterResponse)
+ {
+ return filterResponse;
+ }
HandlerTuple& chosen = cmdIter->second;
if (request->ctx->priv < std::get<Privilege>(chosen))
{
@@ -256,6 +258,11 @@ message::Response::ptr executeIpmiCommandCommon(
cmdIter = handlers.find(wildcard);
if (cmdIter != handlers.end())
{
+ // only return the filter response if the command is found
+ if (filterResponse)
+ {
+ return filterResponse;
+ }
HandlerTuple& chosen = cmdIter->second;
if (request->ctx->priv < std::get<Privilege>(chosen))
{
@@ -270,39 +277,34 @@ message::Response::ptr executeIpmiCommandCommon(
message::Response::ptr executeIpmiGroupCommand(message::Request::ptr request)
{
// look up the group for this request
- Group group;
- if (0 != request->payload.unpack(group))
+ uint8_t bytes;
+ if (0 != request->payload.unpack(bytes))
{
return errorResponse(request, ccReqDataLenInvalid);
}
- // The handler will need to unpack group as well; we just need it for lookup
- request->payload.reset();
+ auto group = static_cast<Group>(bytes);
message::Response::ptr response =
executeIpmiCommandCommon(groupHandlerMap, group, request);
- // if the handler should add the group; executeIpmiCommandCommon does not
- if (response->cc != ccSuccess && response->payload.size() == 0)
- {
- response->pack(group);
- }
+ ipmi::message::Payload prefix;
+ prefix.pack(bytes);
+ response->prepend(prefix);
return response;
}
message::Response::ptr executeIpmiOemCommand(message::Request::ptr request)
{
// look up the iana for this request
- Iana iana;
- if (0 != request->payload.unpack(iana))
+ uint24_t bytes;
+ if (0 != request->payload.unpack(bytes))
{
return errorResponse(request, ccReqDataLenInvalid);
}
- request->payload.reset();
+ auto iana = static_cast<Iana>(bytes);
message::Response::ptr response =
executeIpmiCommandCommon(oemHandlerMap, iana, request);
- // if the handler should add the iana; executeIpmiCommandCommon does not
- if (response->cc != ccSuccess && response->payload.size() == 0)
- {
- response->pack(iana);
- }
+ ipmi::message::Payload prefix;
+ prefix.pack(bytes);
+ response->prepend(prefix);
return response;
}
@@ -320,21 +322,240 @@ message::Response::ptr executeIpmiCommand(message::Request::ptr request)
return executeIpmiCommandCommon(handlerMap, netFn, request);
}
+namespace utils
+{
+template <typename AssocContainer, typename UnaryPredicate>
+void assoc_erase_if(AssocContainer& c, UnaryPredicate p)
+{
+ typename AssocContainer::iterator next = c.begin();
+ typename AssocContainer::iterator last = c.end();
+ while ((next = std::find_if(next, last, p)) != last)
+ {
+ c.erase(next++);
+ }
+}
+} // namespace utils
+
+namespace
+{
+std::unordered_map<std::string, uint8_t> uniqueNameToChannelNumber;
+
+// sdbusplus::bus::match::rules::arg0namespace() wants the prefix
+// to match without any trailing '.'
+constexpr const char ipmiDbusChannelMatch[] =
+ "xyz.openbmc_project.Ipmi.Channel";
+void updateOwners(sdbusplus::asio::connection& conn, const std::string& name)
+{
+ conn.async_method_call(
+ [name](const boost::system::error_code ec,
+ const std::string& nameOwner) {
+ if (ec)
+ {
+ log<level::ERR>("Error getting dbus owner",
+ entry("INTERFACE=%s", name.c_str()));
+ return;
+ }
+ // start after ipmiDbusChannelPrefix (after the '.')
+ std::string chName =
+ name.substr(std::strlen(ipmiDbusChannelMatch) + 1);
+ try
+ {
+ uint8_t channel = getChannelByName(chName);
+ uniqueNameToChannelNumber[nameOwner] = channel;
+ log<level::INFO>("New interface mapping",
+ entry("INTERFACE=%s", name.c_str()),
+ entry("CHANNEL=%u", channel));
+ }
+ catch (const std::exception& e)
+ {
+ log<level::INFO>("Failed interface mapping, no such name",
+ entry("INTERFACE=%s", name.c_str()));
+ }
+ },
+ "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner",
+ name);
+}
+
+void doListNames(boost::asio::io_service& io, sdbusplus::asio::connection& conn)
+{
+ conn.async_method_call(
+ [&io, &conn](const boost::system::error_code ec,
+ std::vector<std::string> busNames) {
+ if (ec)
+ {
+ log<level::ERR>("Error getting dbus names");
+ std::exit(EXIT_FAILURE);
+ return;
+ }
+ // Try to make startup consistent
+ std::sort(busNames.begin(), busNames.end());
+
+ const std::string channelPrefix =
+ std::string(ipmiDbusChannelMatch) + ".";
+ for (const std::string& busName : busNames)
+ {
+ if (busName.find(channelPrefix) == 0)
+ {
+ updateOwners(conn, busName);
+ }
+ }
+ },
+ "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
+ "ListNames");
+}
+
+void nameChangeHandler(sdbusplus::message::message& message)
+{
+ std::string name;
+ std::string oldOwner;
+ std::string newOwner;
+
+ message.read(name, oldOwner, newOwner);
+
+ if (!oldOwner.empty())
+ {
+ if (boost::starts_with(oldOwner, ":"))
+ {
+ // Connection removed
+ auto it = uniqueNameToChannelNumber.find(oldOwner);
+ if (it != uniqueNameToChannelNumber.end())
+ {
+ uniqueNameToChannelNumber.erase(it);
+ }
+ }
+ }
+ if (!newOwner.empty())
+ {
+ // start after ipmiDbusChannelMatch (and after the '.')
+ std::string chName = name.substr(std::strlen(ipmiDbusChannelMatch) + 1);
+ try
+ {
+ uint8_t channel = getChannelByName(chName);
+ uniqueNameToChannelNumber[newOwner] = channel;
+ log<level::INFO>("New interface mapping",
+ entry("INTERFACE=%s", name.c_str()),
+ entry("CHANNEL=%u", channel));
+ }
+ catch (const std::exception& e)
+ {
+ log<level::INFO>("Failed interface mapping, no such name",
+ entry("INTERFACE=%s", name.c_str()));
+ }
+ }
+};
+
+} // anonymous namespace
+
+static constexpr const char intraBmcName[] = "INTRABMC";
+uint8_t channelFromMessage(sdbusplus::message::message& msg)
+{
+ // channel name for ipmitool to resolve to
+ std::string sender = msg.get_sender();
+ auto chIter = uniqueNameToChannelNumber.find(sender);
+ if (chIter != uniqueNameToChannelNumber.end())
+ {
+ return chIter->second;
+ }
+ // FIXME: currently internal connections are ephemeral and hard to pin down
+ try
+ {
+ return getChannelByName(intraBmcName);
+ }
+ catch (const std::exception& e)
+ {
+ return invalidChannel;
+ }
+} // namespace ipmi
+
/* called from sdbus async server context */
-auto executionEntry(boost::asio::yield_context yield, NetFn netFn, uint8_t lun,
+auto executionEntry(boost::asio::yield_context yield,
+ sdbusplus::message::message& m, NetFn netFn, uint8_t lun,
Cmd cmd, std::vector<uint8_t>& data,
std::map<std::string, ipmi::Value>& options)
{
- auto ctx = std::make_shared<ipmi::Context>(netFn, cmd, 0, 0,
- ipmi::Privilege::Admin, &yield);
+ const auto dbusResponse =
+ [netFn, lun, cmd](Cc cc, const std::vector<uint8_t>& data = {}) {
+ constexpr uint8_t netFnResponse = 0x01;
+ uint8_t retNetFn = netFn | netFnResponse;
+ return std::make_tuple(retNetFn, lun, cmd, cc, data);
+ };
+ std::string sender = m.get_sender();
+ Privilege privilege = Privilege::None;
+ int rqSA = 0;
+ uint8_t userId = 0; // undefined user
+ uint32_t sessionId = 0;
+
+ // figure out what channel the request came in on
+ uint8_t channel = channelFromMessage(m);
+ if (channel == invalidChannel)
+ {
+ // unknown sender channel; refuse to service the request
+ log<level::ERR>("ERROR determining source IPMI channel",
+ entry("SENDER=%s", sender.c_str()),
+ entry("NETFN=0x%X", netFn), entry("CMD=0x%X", cmd));
+ return dbusResponse(ipmi::ccDestinationUnavailable);
+ }
+
+ // session-based channels are required to provide userId, privilege and
+ // sessionId
+ if (getChannelSessionSupport(channel) != EChannelSessSupported::none)
+ {
+ try
+ {
+ Value requestPriv = options.at("privilege");
+ Value requestUserId = options.at("userId");
+ Value requestSessionId = options.at("currentSessionId");
+ privilege = static_cast<Privilege>(std::get<int>(requestPriv));
+ userId = static_cast<uint8_t>(std::get<int>(requestUserId));
+ sessionId =
+ static_cast<uint32_t>(std::get<uint32_t>(requestSessionId));
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("ERROR determining IPMI session credentials",
+ entry("CHANNEL=%u", channel),
+ entry("NETFN=0x%X", netFn), entry("CMD=0x%X", cmd));
+ return dbusResponse(ipmi::ccUnspecifiedError);
+ }
+ }
+ else
+ {
+ // get max privilege for session-less channels
+ // For now, there is not a way to configure this, default to Admin
+ privilege = Privilege::Admin;
+
+ // ipmb should supply rqSA
+ ChannelInfo chInfo;
+ getChannelInfo(channel, chInfo);
+ if (static_cast<EChannelMediumType>(chInfo.mediumType) ==
+ EChannelMediumType::ipmb)
+ {
+ const auto iter = options.find("rqSA");
+ if (iter != options.end())
+ {
+ if (std::holds_alternative<int>(iter->second))
+ {
+ rqSA = std::get<int>(iter->second);
+ }
+ }
+ }
+ }
+ // check to see if the requested priv/username is valid
+ log<level::DEBUG>("Set up ipmi context", entry("SENDER=%s", sender.c_str()),
+ entry("NETFN=0x%X", netFn), entry("CMD=0x%X", cmd),
+ entry("CHANNEL=%u", channel), entry("USERID=%u", userId),
+ entry("SESSIONID=0x%X", sessionId),
+ entry("PRIVILEGE=%u", static_cast<uint8_t>(privilege)),
+ entry("RQSA=%x", rqSA));
+
+ auto ctx =
+ std::make_shared<ipmi::Context>(getSdBus(), netFn, cmd, channel, userId,
+ sessionId, privilege, rqSA, yield);
auto request = std::make_shared<ipmi::message::Request>(
ctx, std::forward<std::vector<uint8_t>>(data));
message::Response::ptr response = executeIpmiCommand(request);
- // Responses in IPMI require a bit set. So there ya go...
- netFn |= 0x01;
- return std::make_tuple(netFn, lun, cmd, response->cc,
- response->payload.raw);
+ return dbusResponse(response->cc, response->payload.raw);
}
/** @struct IpmiProvider
@@ -531,29 +752,43 @@ Router* mutableRouter()
/* legacy alternative to executionEntry */
void handleLegacyIpmiCommand(sdbusplus::message::message& m)
{
- unsigned char seq, netFn, lun, cmd;
- std::vector<uint8_t> data;
-
- m.read(seq, netFn, lun, cmd, data);
-
- auto ctx = std::make_shared<ipmi::Context>(netFn, cmd, 0, 0,
- ipmi::Privilege::Admin);
- auto request = std::make_shared<ipmi::message::Request>(
- ctx, std::forward<std::vector<uint8_t>>(data));
- ipmi::message::Response::ptr response = ipmi::executeIpmiCommand(request);
-
- // Responses in IPMI require a bit set. So there ya go...
- netFn |= 0x01;
-
- const char *dest, *path;
- constexpr const char* DBUS_INTF = "org.openbmc.HostIpmi";
-
- dest = m.get_sender();
- path = m.get_path();
- getSdBus()->async_method_call([](boost::system::error_code ec) {}, dest,
- path, DBUS_INTF, "sendMessage", seq, netFn,
- lun, cmd, response->cc,
- response->payload.raw);
+ // make a copy so the next two moves don't wreak havoc on the stack
+ sdbusplus::message::message b{m};
+ boost::asio::spawn(*getIoContext(), [b = std::move(b)](
+ boost::asio::yield_context yield) {
+ sdbusplus::message::message m{std::move(b)};
+ unsigned char seq, netFn, lun, cmd;
+ std::vector<uint8_t> data;
+
+ m.read(seq, netFn, lun, cmd, data);
+ std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
+ auto ctx = std::make_shared<ipmi::Context>(
+ bus, netFn, cmd, 0, 0, 0, ipmi::Privilege::Admin, 0, yield);
+ auto request = std::make_shared<ipmi::message::Request>(
+ ctx, std::forward<std::vector<uint8_t>>(data));
+ ipmi::message::Response::ptr response =
+ ipmi::executeIpmiCommand(request);
+
+ // Responses in IPMI require a bit set. So there ya go...
+ netFn |= 0x01;
+
+ const char *dest, *path;
+ constexpr const char* DBUS_INTF = "org.openbmc.HostIpmi";
+
+ dest = m.get_sender();
+ path = m.get_path();
+ boost::system::error_code ec;
+ bus->yield_method_call(yield, ec, dest, path, DBUS_INTF, "sendMessage",
+ seq, netFn, lun, cmd, response->cc,
+ response->payload.raw);
+ if (ec)
+ {
+ log<level::ERR>("Failed to send response to requestor",
+ entry("ERROR=%s", ec.message().c_str()),
+ entry("SENDER=%s", dest),
+ entry("NETFN=0x%X", netFn), entry("CMD=0x%X", cmd));
+ }
+ });
}
#endif /* ALLOW_DEPRECATED_API */
@@ -591,7 +826,6 @@ int main(int argc, char* argv[])
}
auto sdbusp = std::make_shared<sdbusplus::asio::connection>(*io, bus);
setSdBus(sdbusp);
- sdbusp->request_name("xyz.openbmc_project.Ipmi.Host");
// TODO: Hack to keep the sdEvents running.... Not sure why the sd_event
// queue stops running if we don't have a timer that keeps re-arming
@@ -608,13 +842,6 @@ int main(int argc, char* argv[])
std::forward_list<ipmi::IpmiProvider> providers =
ipmi::loadProviders(HOST_IPMI_LIB_PATH);
- // Add bindings for inbound IPMI requests
- auto server = sdbusplus::asio::object_server(sdbusp);
- auto iface = server.add_interface("/xyz/openbmc_project/Ipmi",
- "xyz.openbmc_project.Ipmi.Server");
- iface->register_method("execute", ipmi::executionEntry);
- iface->initialize();
-
#ifdef ALLOW_DEPRECATED_API
// listen on deprecated signal interface for kcs/bt commands
constexpr const char* FILTER = "type='signal',interface='org.openbmc."
@@ -623,17 +850,36 @@ int main(int argc, char* argv[])
handleLegacyIpmiCommand);
#endif /* ALLOW_DEPRECATED_API */
+ // set up bus name watching to match channels with bus names
+ sdbusplus::bus::match::match nameOwnerChanged(
+ *sdbusp,
+ sdbusplus::bus::match::rules::nameOwnerChanged() +
+ sdbusplus::bus::match::rules::arg0namespace(
+ ipmi::ipmiDbusChannelMatch),
+ ipmi::nameChangeHandler);
+ ipmi::doListNames(*io, *sdbusp);
+
+ int exitCode = 0;
// set up boost::asio signal handling
std::function<SignalResponse(int)> stopAsioRunLoop =
- [&io](int signalNumber) {
+ [&io, &exitCode](int signalNumber) {
log<level::INFO>("Received signal; quitting",
entry("SIGNAL=%d", signalNumber));
io->stop();
+ exitCode = signalNumber;
return SignalResponse::breakExecution;
};
registerSignalHandler(ipmi::prioOpenBmcBase, SIGINT, stopAsioRunLoop);
registerSignalHandler(ipmi::prioOpenBmcBase, SIGTERM, stopAsioRunLoop);
+ sdbusp->request_name("xyz.openbmc_project.Ipmi.Host");
+ // Add bindings for inbound IPMI requests
+ auto server = sdbusplus::asio::object_server(sdbusp);
+ auto iface = server.add_interface("/xyz/openbmc_project/Ipmi",
+ "xyz.openbmc_project.Ipmi.Server");
+ iface->register_method("execute", ipmi::executionEntry);
+ iface->initialize();
+
io->run();
// destroy all the IPMI handlers so the providers can unload safely
@@ -644,5 +890,5 @@ int main(int argc, char* argv[])
// unload the provider libraries
providers.clear();
- return 0;
+ std::exit(exitCode);
}
diff --git a/libipmid/utils.cpp b/libipmid/utils.cpp
index 3ff79ed..ed493d6 100644
--- a/libipmid/utils.cpp
+++ b/libipmid/utils.cpp
@@ -1,6 +1,12 @@
#include <arpa/inet.h>
#include <dirent.h>
+#include <fcntl.h>
+#include <linux/i2c-dev.h>
+#include <linux/i2c.h>
#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <algorithm>
#include <chrono>
@@ -15,7 +21,6 @@ namespace ipmi
using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
-namespace variant_ns = sdbusplus::message::variant_ns;
namespace network
{
@@ -93,43 +98,6 @@ DbusObjectInfo getDbusObject(sdbusplus::bus::bus& bus,
return make_pair(found->first, std::move(found->second.begin()->first));
}
-DbusObjectInfo getIPObject(sdbusplus::bus::bus& bus,
- const std::string& interface,
- const std::string& serviceRoot,
- const std::string& match)
-{
- auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
-
- if (objectTree.empty())
- {
- log<level::ERR>("No Object has implemented the IP interface",
- entry("INTERFACE=%s", interface.c_str()));
- elog<InternalFailure>();
- }
-
- DbusObjectInfo objectInfo;
-
- for (auto& object : objectTree)
- {
- auto variant = ipmi::getDbusProperty(
- bus, object.second.begin()->first, object.first,
- ipmi::network::IP_INTERFACE, "Address");
-
- objectInfo = std::make_pair(object.first, object.second.begin()->first);
-
- // if LinkLocalIP found look for Non-LinkLocalIP
- if (ipmi::network::isLinkLocalIP(variant_ns::get<std::string>(variant)))
- {
- continue;
- }
- else
- {
- break;
- }
- }
- return objectInfo;
-}
-
Value getDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
const std::string& objPath, const std::string& interface,
const std::string& property,
@@ -432,114 +400,62 @@ void callDbusMethod(sdbusplus::bus::bus& bus, const std::string& service,
}
} // namespace method_no_args
-
-namespace network
-{
-
-bool isLinkLocalIP(const std::string& address)
+ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr,
+ std::vector<uint8_t> writeData,
+ std::vector<uint8_t>& readBuf)
{
- return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
-}
-
-void createIP(sdbusplus::bus::bus& bus, const std::string& service,
- const std::string& objPath, const std::string& protocolType,
- const std::string& ipaddress, uint8_t prefix)
-{
- std::string gateway = "";
-
- auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
- IP_CREATE_INTERFACE, "IP");
-
- busMethod.append(protocolType, ipaddress, prefix, gateway);
-
- auto reply = bus.call(busMethod);
-
- if (reply.is_method_error())
+ // Open the i2c device, for low-level combined data write/read
+ int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
+ if (i2cDev < 0)
{
- log<level::ERR>("Failed to execute method", entry("METHOD=%s", "IP"),
- entry("PATH=%s", objPath.c_str()));
- elog<InternalFailure>();
+ log<level::ERR>("Failed to open i2c bus",
+ phosphor::logging::entry("BUS=%s", i2cBus.c_str()));
+ return ipmi::ccInvalidFieldRequest;
}
-}
-
-void createVLAN(sdbusplus::bus::bus& bus, const std::string& service,
- const std::string& objPath, const std::string& interfaceName,
- uint32_t vlanID)
-{
- auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
- VLAN_CREATE_INTERFACE, "VLAN");
- busMethod.append(interfaceName, vlanID);
-
- auto reply = bus.call(busMethod);
-
- if (reply.is_method_error())
+ const size_t writeCount = writeData.size();
+ const size_t readCount = readBuf.size();
+ int msgCount = 0;
+ i2c_msg i2cmsg[2] = {0};
+ if (writeCount)
{
- log<level::ERR>("Failed to execute method", entry("METHOD=%s", "VLAN"),
- entry("PATH=%s", objPath.c_str()));
- elog<InternalFailure>();
+ // Data will be writtern to the slave address
+ i2cmsg[msgCount].addr = slaveAddr;
+ i2cmsg[msgCount].flags = 0x00;
+ i2cmsg[msgCount].len = writeCount;
+ i2cmsg[msgCount].buf = writeData.data();
+ msgCount++;
}
-}
-
-uint8_t toPrefix(int addressFamily, const std::string& subnetMask)
-{
- if (addressFamily == AF_INET6)
+ if (readCount)
{
- return 0;
+ // Data will be read into the buffer from the slave address
+ i2cmsg[msgCount].addr = slaveAddr;
+ i2cmsg[msgCount].flags = I2C_M_RD;
+ i2cmsg[msgCount].len = readCount;
+ i2cmsg[msgCount].buf = readBuf.data();
+ msgCount++;
}
- uint32_t buff{};
+ i2c_rdwr_ioctl_data msgReadWrite = {0};
+ msgReadWrite.msgs = i2cmsg;
+ msgReadWrite.nmsgs = msgCount;
- auto rc = inet_pton(addressFamily, subnetMask.c_str(), &buff);
- if (rc <= 0)
- {
- log<level::ERR>("inet_pton failed:",
- entry("SUBNETMASK=%s", subnetMask.c_str()));
- return 0;
- }
+ // Perform the combined write/read
+ int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
+ ::close(i2cDev);
- buff = be32toh(buff);
- // total no of bits - total no of leading zero == total no of ones
- if (((sizeof(buff) * 8) - (__builtin_ctz(buff))) ==
- __builtin_popcount(buff))
+ if (ret < 0)
{
- return __builtin_popcount(buff);
+ log<level::ERR>("I2C WR Failed!",
+ phosphor::logging::entry("RET=%d", ret));
+ return ipmi::ccUnspecifiedError;
}
- else
+ if (readCount)
{
- log<level::ERR>("Invalid Mask",
- entry("SUBNETMASK=%s", subnetMask.c_str()));
- return 0;
+ readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
}
-}
-
-uint32_t getVLAN(const std::string& path)
-{
- // Path would be look like
- // /xyz/openbmc_project/network/eth0_443/ipv4
-
- uint32_t vlanID = 0;
- try
- {
- auto intfObjectPath = path.substr(0, path.find(IP_TYPE) - 1);
-
- auto intfName = intfObjectPath.substr(intfObjectPath.rfind("/") + 1);
- auto index = intfName.find("_");
- if (index != std::string::npos)
- {
- auto str = intfName.substr(index + 1);
- vlanID = std::stoul(str);
- }
- }
- catch (std::exception& e)
- {
- log<level::ERR>("Exception occurred during getVLAN",
- entry("PATH=%s", path.c_str()),
- entry("EXCEPTION=%s", e.what()));
- }
- return vlanID;
+ return ipmi::ccSuccess;
}
-} // namespace network
} // namespace ipmi
diff --git a/oemrouter.cpp b/oemrouter.cpp
deleted file mode 100644
index 00f66f1..0000000
--- a/oemrouter.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-#include <cstdio>
-#include <cstring>
-#include <ipmid/oemrouter.hpp>
-#include <map>
-#include <utility>
-
-namespace oem
-{
-
-using Key = std::pair<Number, ipmi_cmd_t>;
-
-// Private implementation of OemRouter Interface.
-class RouterImpl : public Router
-{
- public:
- RouterImpl()
- {
- }
-
- // Implement OemRouter Interface.
- void activate() override;
- void registerHandler(Number oen, ipmi_cmd_t cmd, Handler handler) override;
-
- // Actual message routing function.
- ipmi_ret_t routeMsg(ipmi_cmd_t cmd, const uint8_t* reqBuf,
- uint8_t* replyBuf, size_t* dataLen);
-
- private:
- std::map<Key, Handler> handlers;
-};
-
-// Static global instance for simplicity.
-static RouterImpl* globalRouterImpl;
-
-// TODO Refactor ipmid to avoid need for singleton here.
-Router* mutableRouter()
-{
- if (!globalRouterImpl)
- {
- globalRouterImpl = new RouterImpl;
- }
- return globalRouterImpl;
-}
-
-ipmi_ret_t RouterImpl::routeMsg(ipmi_cmd_t cmd, const uint8_t* reqBuf,
- uint8_t* replyBuf, size_t* dataLen)
-{
- // Not entirely clear we can route reply without complete OEM group.
- // TODO: consider adding a way to suppress malformed replies.
- if (*dataLen < groupMagicSize)
- {
- std::fprintf(stderr, "NetFn:[0x2E], OEM:[%zu bytes?], Cmd:[%#04X]\n",
- *dataLen, cmd);
- (*dataLen) = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- // Find registered handler or reject request.
- auto number = toOemNumber(reqBuf);
- auto cmdKey = std::make_pair(number, cmd);
-
- auto iter = handlers.find(cmdKey);
- if (iter == handlers.end())
- {
- auto wildKey = std::make_pair(number, IPMI_CMD_WILDCARD);
- iter = handlers.find(wildKey);
- if (iter == handlers.end())
- {
- std::fprintf(stderr,
- "No Registered handler for NetFn:[0x2E], "
- "OEM:[%#08X], Cmd:[%#04X]\n",
- number, cmd);
- *dataLen = groupMagicSize;
- return IPMI_CC_INVALID;
- }
-#ifdef __IPMI_DEBUG__
- std::fprintf(stderr,
- "Wildcard NetFn:[0x2E], OEM:[%#08X], Cmd:[%#04X]\n",
- number, cmd);
-#endif
- }
- else
- {
-#ifdef __IPMI_DEBUG__
- std::fprintf(stderr, "Match NetFn:[0x2E], OEM:[%#08X], Cmd:[%#04X]\n",
- number, cmd);
-#endif
- }
-
- // Copy OEMGroup here, by analogy to IPMI CC code at netfn router;
- // OemHandler should deal only with optional following data bytes.
- std::memcpy(replyBuf, reqBuf, groupMagicSize);
-
- size_t oemDataLen = *dataLen - groupMagicSize;
- Handler& handler = iter->second;
-
- auto rc = handler(cmd, reqBuf + groupMagicSize, replyBuf + groupMagicSize,
- &oemDataLen);
-
- // Add OEMGroup bytes to nominal reply.
- *dataLen = oemDataLen + groupMagicSize;
- return rc;
-}
-
-// Function suitable for use as ipmi_netfn_router() call-back.
-// Translates call-back pointer args to more specific types.
-ipmi_ret_t ipmi_oem_wildcard_handler(ipmi_netfn_t /* netfn */, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t dataLen,
- ipmi_context_t context)
-{
- // View requests & responses as byte sequences.
- const uint8_t* reqBuf = static_cast<uint8_t*>(request);
- uint8_t* replyBuf = static_cast<uint8_t*>(response);
-
- // View context as router object, defaulting nullptr to global object.
- auto router = static_cast<RouterImpl*>(context ? context : mutableRouter());
-
- // Send message parameters to dispatcher.
- return router->routeMsg(cmd, reqBuf, replyBuf, dataLen);
-}
-
-// Enable message routing to begin.
-void RouterImpl::activate()
-{
- // Register netfn 0x2e OEM Group, any (wildcard) command.
- std::printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_OEM_GROUP,
- IPMI_CMD_WILDCARD);
- ipmi_register_callback(NETFUN_OEM_GROUP, IPMI_CMD_WILDCARD, this,
- ipmi_oem_wildcard_handler, PRIVILEGE_OEM);
-}
-
-void RouterImpl::registerHandler(Number oen, ipmi_cmd_t cmd, Handler handler)
-{
- auto cmdKey = std::make_pair(oen, cmd);
- auto iter = handlers.find(cmdKey);
- if (iter == handlers.end())
- {
- // Add handler if key not already taken.
- handlers.emplace(cmdKey, handler);
- }
- else
- {
- std::fprintf(stderr,
- "ERROR : Duplicate registration for NetFn:[0x2E], "
- "OEM:[%#08X], Cmd:[%#04X]\n",
- oen, cmd);
- }
-}
-
-} // namespace oem
diff --git a/read_fru_data.cpp b/read_fru_data.cpp
index 1f99e20..270accf 100644
--- a/read_fru_data.cpp
+++ b/read_fru_data.cpp
@@ -17,14 +17,13 @@ namespace ipmi
namespace fru
{
-namespace variant_ns = sdbusplus::message::variant_ns;
-
using namespace phosphor::logging;
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
std::unique_ptr<sdbusplus::bus::match_t> matchPtr
__attribute__((init_priority(101)));
+static constexpr auto XYZ_PREFIX = "/xyz/openbmc_project/";
static constexpr auto INV_INTF = "xyz.openbmc_project.Inventory.Manager";
static constexpr auto OBJ_PATH = "/xyz/openbmc_project/inventory";
static constexpr auto PROP_INTF = "org.freedesktop.DBus.Properties";
@@ -51,21 +50,38 @@ ipmi::PropertyMap readAllProperties(const std::string& intf,
{
ipmi::PropertyMap properties;
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
- auto service = ipmi::getService(bus, INV_INTF, OBJ_PATH);
- std::string objPath = OBJ_PATH + path;
+ std::string service;
+ std::string objPath;
+
+ // Is the path the full dbus path?
+ if (path.find(XYZ_PREFIX) != std::string::npos)
+ {
+ service = ipmi::getService(bus, intf, path);
+ objPath = path;
+ }
+ else
+ {
+ service = ipmi::getService(bus, INV_INTF, OBJ_PATH);
+ objPath = OBJ_PATH + path;
+ }
+
auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
PROP_INTF, "GetAll");
method.append(intf);
- auto reply = bus.call(method);
- if (reply.is_method_error())
+ try
+ {
+ auto reply = bus.call(method);
+ reply.read(properties);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
{
// If property is not found simply return empty value
- log<level::ERR>("Error in reading property values from inventory",
+ log<level::ERR>("Error in reading property values",
+ entry("EXCEPTION=%s", e.what()),
entry("INTERFACE=%s", intf.c_str()),
entry("PATH=%s", objPath.c_str()));
- return properties;
}
- reply.read(properties);
+
return properties;
}
@@ -140,9 +156,9 @@ FruInventoryData readDataFromInventory(const FRUId& fruNum)
if (iter != allProp.end())
{
data[properties.second.section].emplace(
- properties.first,
- std::move(variant_ns::get<std::string>(
- allProp[properties.first])));
+ properties.second.property,
+ std::move(
+ std::get<std::string>(allProp[properties.first])));
}
}
}
diff --git a/scripts/entity-example.yaml b/scripts/entity-example.md
index 2db14cc..98dbade 100755..100644
--- a/scripts/entity-example.yaml
+++ b/scripts/entity-example.md
@@ -1,3 +1,28 @@
+If your platform requires the entity container map, you can provide a json file of the format:
+
+```
+[
+ {
+ "id" : 1,
+ "containerEntityId" : 2,
+ "containerEntityInstance" : 3,
+ "isList" : false,
+ "isLinked" : false,
+ "entities" : [
+ {"id" : 1, "instance" : 2},
+ {"id" : 1, "instance" : 3},
+ {"id" : 1, "instance" : 4},
+ {"id" : 1, "instance" : 5}
+ ]
+ }
+]
+```
+
+as part of your `phosphor-ipmi-config`
+
+The above json is identical to the original YAML documented below:
+
+```
# This record has:
# Container Entity Id and Container Entity Instance = (0x13, 0x81)
# Contained Entity Id and Contained Entity Instance = (0x0A, 0x1),
@@ -125,3 +150,4 @@
entityInstance3: 0xD
entityId4: 0x20
entityInstance4: 0xF
+```
diff --git a/scripts/entity_gen.py b/scripts/entity_gen.py
deleted file mode 100755
index 057821b..0000000
--- a/scripts/entity_gen.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env python
-
-import os
-import sys
-import yaml
-import argparse
-from mako.template import Template
-
-
-def generate_cpp(entity_yaml, output_dir):
- with open(os.path.join(script_dir, entity_yaml), 'r') as f:
- ifile = yaml.safe_load(f)
- if not isinstance(ifile, dict):
- ifile = {}
-
- # Render the mako template
-
- t = Template(filename=os.path.join(
- script_dir,
- "writeentity.mako.cpp"))
-
- output_cpp = os.path.join(output_dir, "entity-gen.cpp")
- with open(output_cpp, 'w') as fd:
- fd.write(t.render(entityDict=ifile))
-
-
-def main():
-
- valid_commands = {
- 'generate-cpp': generate_cpp
- }
- parser = argparse.ArgumentParser(
- description="IPMI Entity record parser and code generator")
-
- parser.add_argument(
- '-i', '--entity_yaml', dest='entity_yaml',
- default='example.yaml', help='input entity yaml file to parse')
-
- parser.add_argument(
- "-o", "--output-dir", dest="outputdir",
- default=".",
- help="output directory")
-
- parser.add_argument(
- 'command', metavar='COMMAND', type=str,
- choices=valid_commands.keys(),
- help='Command to run.')
-
- args = parser.parse_args()
-
- if (not (os.path.isfile(os.path.join(script_dir, args.entity_yaml)))):
- sys.exit("Can not find input yaml file " + args.entity_yaml)
-
- function = valid_commands[args.command]
- function(args.entity_yaml, args.outputdir)
-
-
-if __name__ == '__main__':
- script_dir = os.path.dirname(os.path.realpath(__file__))
- main()
diff --git a/scripts/fru_gen.py b/scripts/fru_gen.py
index f6111b7..a8d148a 100755
--- a/scripts/fru_gen.py
+++ b/scripts/fru_gen.py
@@ -8,7 +8,7 @@ from mako.template import Template
def generate_cpp(inventory_yaml, output_dir):
- with open(os.path.join(script_dir, inventory_yaml), 'r') as f:
+ with open(inventory_yaml, 'r') as f:
ifile = yaml.safe_load(f)
if not isinstance(ifile, dict):
ifile = {}
@@ -48,7 +48,7 @@ def main():
args = parser.parse_args()
- if (not (os.path.isfile(os.path.join(script_dir, args.inventory_yaml)))):
+ if (not (os.path.isfile(args.inventory_yaml))):
sys.exit("Can not find input yaml file " + args.inventory_yaml)
function = valid_commands[args.command]
diff --git a/scripts/inventory-sensor-example.yaml b/scripts/inventory-sensor-example.yaml
index 7ff78fb..7ff78fb 100755..100644
--- a/scripts/inventory-sensor-example.yaml
+++ b/scripts/inventory-sensor-example.yaml
diff --git a/scripts/inventory-sensor.py b/scripts/inventory-sensor.py
index 77222f5..2dd1e8d 100755
--- a/scripts/inventory-sensor.py
+++ b/scripts/inventory-sensor.py
@@ -8,7 +8,7 @@ from mako.template import Template
def generate_cpp(sensor_yaml, output_dir):
- with open(os.path.join(script_dir, sensor_yaml), 'r') as f:
+ with open(sensor_yaml, 'r') as f:
ifile = yaml.safe_load(f)
if not isinstance(ifile, dict):
ifile = {}
@@ -48,7 +48,7 @@ def main():
args = parser.parse_args()
- if (not (os.path.isfile(os.path.join(script_dir, args.sensor_yaml)))):
+ if (not (os.path.isfile(args.sensor_yaml))):
sys.exit("Can not find input yaml file " + args.sensor_yaml)
function = valid_commands[args.command]
diff --git a/scripts/sensor-example.yaml b/scripts/sensor-example.yaml
index 4a1472d..c0fbe1e 100755..100644
--- a/scripts/sensor-example.yaml
+++ b/scripts/sensor-example.yaml
@@ -154,3 +154,41 @@
#the update will be skipped.
skipOn: deassert
type: bool
+
+0xC5:
+ sensorType: 0x17
+ path: /system/chassis/motherboard/gv100card0
+ sensorReadingType: 1
+ serviceInterface: xyz.openbmc_project.Inventory.Manager
+ readingType: assertion
+ mutability: Mutability::Write|Mutability::Read
+ sensorNamePattern: nameLeaf
+ interfaces:
+ xyz.openbmc_project.Inventory.Decorator.Replaceable:
+ FieldReplaceable:
+ Offsets:
+ 7:
+ assert: true
+ deassert: true
+ type: bool
+ xyz.openbmc_project.Inventory.Item:
+ Present:
+ Offsets:
+ 7:
+ assert: true
+ deassert: false
+ type: bool
+ # Example of an interface with no attached properties
+ xyz.openbmc_project.Inventory.Item.Accelerator:
+ xyz.openbmc_project.State.Decorator.OperationalStatus:
+ Functional:
+ Offsets:
+ 8:
+ assert: false
+ deassert: true
+ type: bool
+ Prereqs:
+ 7:
+ assert: true
+ deassert: false
+ type: bool
diff --git a/scripts/sensor_gen.py b/scripts/sensor_gen.py
index 822a00a..6f59021 100755
--- a/scripts/sensor_gen.py
+++ b/scripts/sensor_gen.py
@@ -8,7 +8,7 @@ from mako.template import Template
def generate_cpp(sensor_yaml, output_dir):
- with open(os.path.join(script_dir, sensor_yaml), 'r') as f:
+ with open(sensor_yaml, 'r') as f:
ifile = yaml.safe_load(f)
if not isinstance(ifile, dict):
ifile = {}
@@ -48,7 +48,7 @@ def main():
args = parser.parse_args()
- if (not (os.path.isfile(os.path.join(script_dir, args.sensor_yaml)))):
+ if (not (os.path.isfile(args.sensor_yaml))):
sys.exit("Can not find input yaml file " + args.sensor_yaml)
function = valid_commands[args.command]
diff --git a/scripts/writeentity.mako.cpp b/scripts/writeentity.mako.cpp
deleted file mode 100644
index 4cfc82b..0000000
--- a/scripts/writeentity.mako.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-## This file is a template. The comment below is emitted
-## into the rendered file; feel free to edit this file.
-// !!! WARNING: This is a GENERATED Code..Please do NOT Edit !!!
-
-#include <ipmid/types.hpp>
-using namespace ipmi::sensor;
-
-extern const EntityInfoMap entities = {
-% for key in entityDict.iterkeys():
-{${key},{
-<%
- entity = entityDict[key]
- containerEntityId = entity["containerEntityId"]
- containerEntityInstance = entity["containerEntityInstance"]
- isList = entity["isList"]
- isLinked = entity["isLinked"]
- entityId1 = entity["entityId1"]
- entityInstance1 = entity["entityInstance1"]
- entityId2 = entity["entityId2"]
- entityInstance2 = entity["entityInstance2"]
- entityId3 = entity["entityId3"]
- entityInstance3 = entity["entityInstance3"]
- entityId4 = entity["entityId4"]
- entityInstance4 = entity["entityInstance4"]
-%>
- ${containerEntityId},${containerEntityInstance},${isList},${isLinked},{
- std::make_pair(${entityId1}, ${entityInstance1}),
- std::make_pair(${entityId2}, ${entityInstance2}),
- std::make_pair(${entityId3}, ${entityInstance3}),
- std::make_pair(${entityId4}, ${entityInstance4}) }
-
-}},
-% endfor
-};
diff --git a/scripts/writesensor.mako.cpp b/scripts/writesensor.mako.cpp
index 559f0f9..c740f6f 100644
--- a/scripts/writesensor.mako.cpp
+++ b/scripts/writesensor.mako.cpp
@@ -27,7 +27,9 @@ interfaceDict = {}
#include "sensordatahandler.hpp"
#include <ipmid/types.hpp>
-using namespace ipmi::sensor;
+
+namespace ipmi {
+namespace sensor {
extern const IdInfoMap sensors = {
% for key in sensorDict.iterkeys():
@@ -74,67 +76,69 @@ extern const IdInfoMap sensors = {
${updateFunc},${getFunc},Mutability(${mutability}),${sensorNameFunc},{
% for interface,properties in interfaces.items():
{"${interface}",{
- % for dbus_property,property_value in properties.items():
- {"${dbus_property}",{
+ % if properties:
+ % for dbus_property,property_value in properties.items():
+ {"${dbus_property}",{
<%
try:
preReq = property_value["Prereqs"]
except KeyError, e:
preReq = dict()
%>\
- {
- % for preOffset,preValues in preReq.items():
- { ${preOffset},{
- % for name,value in preValues.items():
- % if name == "type":
+ {
+ % for preOffset,preValues in preReq.items():
+ { ${preOffset},{
+ % for name,value in preValues.items():
+ % if name == "type":
<% continue %>\
- % endif
+ % endif
<% value = str(value).lower() %>\
- ${value},
+ ${value},
+ % endfor
+ }
+ },
% endfor
- }
},
- % endfor
- },
- {
- % for offset,values in property_value["Offsets"].items():
- { ${offset},{
- % if offset == 0xFF:
- }},
+ {
+ % for offset,values in property_value["Offsets"].items():
+ { ${offset},{
+ % if offset == 0xFF:
+ }},
<% continue %>\
- % endif
+ % endif
<% valueType = values["type"] %>\
<%
try:
skip = values["skipOn"]
if skip == "assert":
- skipVal = "SkipAssertion::ASSERT"
+ skipVal = "SkipAssertion::ASSERT"
elif skip == "deassert":
- skipVal = "SkipAssertion::DEASSERT"
+ skipVal = "SkipAssertion::DEASSERT"
else:
- assert "Unknown skip value " + str(skip)
+ assert "Unknown skip value " + str(skip)
except KeyError, e:
skipVal = "SkipAssertion::NONE"
%>\
- ${skipVal},
- % for name,value in values.items():
- % if name == "type" or name == "skipOn":
+ ${skipVal},
+ % for name,value in values.items():
+ % if name == "type" or name == "skipOn":
<% continue %>\
- % endif
- % if valueType == "string":
- std::string("${value}"),
- % elif valueType == "bool":
+ % endif
+ % if valueType == "string":
+ std::string("${value}"),
+ % elif valueType == "bool":
<% value = str(value).lower() %>\
- ${value},
- % else:
- ${value},
- % endif
- % endfor
- }
- },
+ ${value},
+ % else:
+ ${value},
+ % endif
+ % endfor
+ }
+ },
+ % endfor
+ }}},
% endfor
- }}},
- % endfor
+ % endif
}},
% endfor
},
@@ -143,3 +147,5 @@ except KeyError, e:
% endfor
};
+} // namespace sensor
+} // namespace ipmi
diff --git a/selutility.cpp b/selutility.cpp
index 97c2b2b..cd3b6a6 100644
--- a/selutility.cpp
+++ b/selutility.cpp
@@ -58,8 +58,7 @@ GetSELEntryResponse
elog<InternalFailure>();
}
- record.recordID = static_cast<uint16_t>(
- sdbusplus::message::variant_ns::get<uint32_t>(iterId->second));
+ record.recordID = static_cast<uint16_t>(std::get<uint32_t>(iterId->second));
// Read Timestamp from the log entry.
static constexpr auto propTimeStamp = "Timestamp";
@@ -71,7 +70,7 @@ GetSELEntryResponse
}
std::chrono::milliseconds chronoTimeStamp(
- sdbusplus::message::variant_ns::get<uint64_t>(iterTimeStamp->second));
+ std::get<uint64_t>(iterTimeStamp->second));
record.timeStamp = static_cast<uint32_t>(
std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp)
.count());
@@ -100,7 +99,7 @@ GetSELEntryResponse
static constexpr auto deassertEvent = 0x80;
// Evaluate if the event is assertion or deassertion event
- if (sdbusplus::message::variant_ns::get<bool>(iterResolved->second))
+ if (std::get<bool>(iterResolved->second))
{
record.eventType = deassertEvent | iter->second.eventReadingType;
}
@@ -118,8 +117,9 @@ GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath)
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
- static constexpr auto assocIntf = "org.openbmc.Associations";
- static constexpr auto assocProp = "associations";
+ static constexpr auto assocIntf =
+ "xyz.openbmc_project.Association.Definitions";
+ static constexpr auto assocProp = "Associations";
auto service = ipmi::getService(bus, assocIntf, objPath);
@@ -139,10 +139,10 @@ GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath)
using AssociationList =
std::vector<std::tuple<std::string, std::string, std::string>>;
- sdbusplus::message::variant<AssociationList> list;
+ std::variant<AssociationList> list;
reply.read(list);
- auto& assocs = sdbusplus::message::variant_ns::get<AssociationList>(list);
+ auto& assocs = std::get<AssociationList>(list);
/*
* Check if the log entry has any callout associations, if there is a
@@ -201,11 +201,10 @@ std::chrono::seconds getEntryTimeStamp(const std::string& objPath)
elog<InternalFailure>();
}
- sdbusplus::message::variant<uint64_t> timeStamp;
+ std::variant<uint64_t> timeStamp;
reply.read(timeStamp);
- std::chrono::milliseconds chronoTimeStamp(
- sdbusplus::message::variant_ns::get<uint64_t>(timeStamp));
+ std::chrono::milliseconds chronoTimeStamp(std::get<uint64_t>(timeStamp));
return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp);
}
diff --git a/selutility.hpp b/selutility.hpp
index 806f937..49ec1b7 100644
--- a/selutility.hpp
+++ b/selutility.hpp
@@ -28,32 +28,26 @@ using Id = uint32_t;
using Timestamp = uint64_t;
using Message = std::string;
using AdditionalData = std::vector<std::string>;
-using PropertyType = sdbusplus::message::variant<Resolved, Id, Timestamp,
- Message, AdditionalData>;
+using PropertyType =
+ std::variant<Resolved, Id, Timestamp, Message, AdditionalData>;
static constexpr auto selVersion = 0x51;
static constexpr auto invalidTimeStamp = 0xFFFFFFFF;
-static constexpr auto operationSupport = 0x0A;
-
-/** @struct GetSELInfoResponse
- *
- * IPMI payload for Get SEL Info command response.
- */
-struct GetSELInfoResponse
-{
- uint8_t selVersion; //!< SEL revision.
- uint16_t entries; //!< Number of log entries in SEL.
- uint16_t freeSpace; //!< Free Space in bytes.
- uint32_t addTimeStamp; //!< Most recent addition timestamp.
- uint32_t eraseTimeStamp; //!< Most recent erase timestamp.
- uint8_t operationSupport; //!< Operation support.
-} __attribute__((packed));
static constexpr auto firstEntry = 0x0000;
static constexpr auto lastEntry = 0xFFFF;
static constexpr auto entireRecord = 0xFF;
static constexpr auto selRecordSize = 16;
+namespace operationSupport
+{
+static constexpr bool overflow = false;
+static constexpr bool deleteSel = true;
+static constexpr bool partialAddSelEntry = false;
+static constexpr bool reserveSel = true;
+static constexpr bool getSelAllocationInfo = false;
+} // namespace operationSupport
+
/** @struct GetSELEntryRequest
*
* IPMI payload for Get SEL Entry command request.
@@ -86,33 +80,10 @@ struct GetSELEntryResponse
uint8_t eventData3; //!< Event Data 3.
} __attribute__((packed));
-/** @struct DeleteSELEntryRequest
- *
- * IPMI payload for Delete SEL Entry command request.
- */
-struct DeleteSELEntryRequest
-{
- uint16_t reservationID; //!< Reservation ID.
- uint16_t selRecordID; //!< SEL Record ID.
-} __attribute__((packed));
-
static constexpr auto initiateErase = 0xAA;
static constexpr auto getEraseStatus = 0x00;
static constexpr auto eraseComplete = 0x01;
-/** @struct ClearSELRequest
- *
- * IPMI payload for Clear SEL command request.
- */
-struct ClearSELRequest
-{
- uint16_t reservationID; //!< Reservation ID.
- uint8_t charC; //!< Char 'C'(0x43h).
- uint8_t charL; //!< Char 'L'(0x4Ch).
- uint8_t charR; //!< Char 'R'(0x52h).
- uint8_t eraseOperation; //!< Erase operation.
-} __attribute__((packed));
-
/** @brief Convert logging entry to SEL
*
* @param[in] objPath - DBUS object path of the logging entry.
diff --git a/sensordatahandler.cpp b/sensordatahandler.cpp
index 1de1540..0d1182d 100644
--- a/sensordatahandler.cpp
+++ b/sensordatahandler.cpp
@@ -17,8 +17,6 @@ namespace ipmi
namespace sensor
{
-namespace variant_ns = sdbusplus::message::variant_ns;
-
using namespace phosphor::logging;
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
@@ -134,7 +132,6 @@ GetSensorResponse mapDbusToAssertion(const Info& sensorInfo,
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
GetSensorResponse response{};
- auto responseData = reinterpret_cast<GetReadingResponse*>(response.data());
auto service = ipmi::getService(bus, interface, path);
@@ -151,7 +148,7 @@ GetSensorResponse mapDbusToAssertion(const Info& sensorInfo,
{
if (propValue == value.second.assert)
{
- setOffset(value.first, responseData);
+ setOffset(value.first, &response);
break;
}
}
@@ -171,7 +168,6 @@ GetSensorResponse eventdata2(const Info& sensorInfo)
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
GetSensorResponse response{};
- auto responseData = reinterpret_cast<GetReadingResponse*>(response.data());
auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
sensorInfo.sensorPath);
@@ -190,7 +186,7 @@ GetSensorResponse eventdata2(const Info& sensorInfo)
{
if (propValue == value.second.assert)
{
- setReading(value.first, responseData);
+ setReading(value.first, &response);
break;
}
}
@@ -324,6 +320,15 @@ ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
ipmi::sensor::InterfaceMap interfaces;
for (const auto& interface : sensorInfo.propertyInterfaces)
{
+ // An interface with no properties - It is possible that the sensor
+ // object on DBUS implements a DBUS interface with no properties.
+ // Make sure we add the interface to the list if interfaces on the
+ // object with an empty property map.
+ if (interface.second.empty())
+ {
+ interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{});
+ continue;
+ }
// For a property like functional state the result will be
// calculated based on the true value of all conditions.
for (const auto& property : interface.second)
@@ -340,8 +345,7 @@ ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
{
return IPMI_CC_OK;
}
- result =
- result && variant_ns::get<bool>(value.second.assert);
+ result = result && std::get<bool>(value.second.assert);
valid = true;
}
else if (deassertionSet.test(value.first))
@@ -351,8 +355,7 @@ ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
{
return IPMI_CC_OK;
}
- result =
- result && variant_ns::get<bool>(value.second.deassert);
+ result = result && std::get<bool>(value.second.deassert);
valid = true;
}
}
@@ -361,13 +364,11 @@ ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
{
if (assertionSet.test(value.first))
{
- result =
- result && variant_ns::get<bool>(value.second.assert);
+ result = result && std::get<bool>(value.second.assert);
}
else if (deassertionSet.test(value.first))
{
- result =
- result && variant_ns::get<bool>(value.second.deassert);
+ result = result && std::get<bool>(value.second.deassert);
}
}
if (valid)
diff --git a/sensordatahandler.hpp b/sensordatahandler.hpp
index 6d80f9a..99c3ae9 100644
--- a/sensordatahandler.hpp
+++ b/sensordatahandler.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include "config.h"
+
#include "sensorhandler.hpp"
#include <cmath>
@@ -13,8 +15,6 @@ namespace ipmi
namespace sensor
{
-namespace variant_ns = sdbusplus::message::variant_ns;
-
using Assertion = uint16_t;
using Deassertion = uint16_t;
using AssertionSet = std::pair<Assertion, Deassertion>;
@@ -156,7 +156,6 @@ GetSensorResponse readingAssertion(const Info& sensorInfo)
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
GetSensorResponse response{};
- auto responseData = reinterpret_cast<GetReadingResponse*>(response.data());
auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
sensorInfo.sensorPath);
@@ -166,8 +165,7 @@ GetSensorResponse readingAssertion(const Info& sensorInfo)
sensorInfo.propertyInterfaces.begin()->first,
sensorInfo.propertyInterfaces.begin()->second.begin()->first);
- setAssertionBytes(static_cast<uint16_t>(variant_ns::get<T>(propValue)),
- responseData);
+ setAssertionBytes(static_cast<uint16_t>(std::get<T>(propValue)), &response);
return response;
}
@@ -184,26 +182,51 @@ template <typename T>
GetSensorResponse readingData(const Info& sensorInfo)
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+
GetSensorResponse response{};
- auto responseData = reinterpret_cast<GetReadingResponse*>(response.data());
- enableScanning(responseData);
+ enableScanning(&response);
auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
sensorInfo.sensorPath);
+#ifdef UPDATE_FUNCTIONAL_ON_FAIL
+ // Check the OperationalStatus interface for functional property
+ if (sensorInfo.propertyInterfaces.begin()->first ==
+ "xyz.openbmc_project.Sensor.Value")
+ {
+ bool functional = true;
+ try
+ {
+ auto funcValue = ipmi::getDbusProperty(
+ bus, service, sensorInfo.sensorPath,
+ "xyz.openbmc_project.State.Decorator.OperationalStatus",
+ "Functional");
+ functional = std::get<bool>(funcValue);
+ }
+ catch (...)
+ {
+ // No-op if Functional property could not be found since this
+ // check is only valid for Sensor.Value read for hwmonio
+ }
+ if (!functional)
+ {
+ throw SensorFunctionalError();
+ }
+ }
+#endif
+
auto propValue = ipmi::getDbusProperty(
bus, service, sensorInfo.sensorPath,
sensorInfo.propertyInterfaces.begin()->first,
sensorInfo.propertyInterfaces.begin()->second.begin()->first);
- double value = variant_ns::get<T>(propValue) *
+ double value = std::get<T>(propValue) *
std::pow(10, sensorInfo.scale - sensorInfo.exponentR);
auto rawData = static_cast<uint8_t>((value - sensorInfo.scaledOffset) /
sensorInfo.coefficientM);
-
- setReading(rawData, responseData);
+ setReading(rawData, &response);
return response;
}
@@ -252,7 +275,7 @@ ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData,
for (const auto& property : interface->second)
{
msg.append(property.first);
- sdbusplus::message::variant<T> value =
+ std::variant<T> value =
(cmdData.assertOffset8_14 << 8) | cmdData.assertOffset0_7;
msg.append(value);
}
@@ -283,7 +306,7 @@ ipmi_ret_t readingData(const SetSensorReadingReq& cmdData,
for (const auto& property : interface->second)
{
msg.append(property.first);
- sdbusplus::message::variant<T> value = raw_value;
+ std::variant<T> value = raw_value;
msg.append(value);
}
return updateToDbus(msg);
diff --git a/sensorhandler.cpp b/sensorhandler.cpp
index 48546af..1cffd55 100644
--- a/sensorhandler.cpp
+++ b/sensorhandler.cpp
@@ -1,5 +1,8 @@
+#include "config.h"
+
#include "sensorhandler.hpp"
+#include "entity_map_json.hpp"
#include "fruread.hpp"
#include <mapper.h>
@@ -24,16 +27,21 @@ static constexpr uint8_t BMCSlaveAddress = 0x20;
extern int updateSensorRecordFromSSRAESC(const void*);
extern sd_bus* bus;
-extern const ipmi::sensor::IdInfoMap sensors;
+
+namespace ipmi
+{
+namespace sensor
+{
+extern const IdInfoMap sensors;
+} // namespace sensor
+} // namespace ipmi
+
extern const FruMap frus;
-extern const ipmi::sensor::EntityInfoMap entities;
using namespace phosphor::logging;
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
-namespace variant_ns = sdbusplus::message::variant_ns;
-
void register_netfn_sen_functions() __attribute__((constructor));
struct sensorTypemap_t
@@ -71,13 +79,6 @@ struct sensor_data_t
uint8_t sennum;
} __attribute__((packed));
-struct sensorreadingresp_t
-{
- uint8_t value;
- uint8_t operation;
- uint8_t indication[2];
-} __attribute__((packed));
-
int get_bus_for_path(const char* path, char** busname)
{
return mapper_get_service(bus, path, busname);
@@ -90,8 +91,8 @@ int find_openbmc_path(uint8_t num, dbus_interface_t* interface)
{
int rc;
- const auto& sensor_it = sensors.find(num);
- if (sensor_it == sensors.end())
+ const auto& sensor_it = ipmi::sensor::sensors.find(num);
+ if (sensor_it == ipmi::sensor::sensors.end())
{
// The sensor map does not contain the sensor requested
return -EINVAL;
@@ -324,16 +325,54 @@ bool isAnalogSensor(const std::string& interface)
return (analogSensorInterfaces.count(interface));
}
-ipmi_ret_t setSensorReading(void* request)
+/**
+@brief This command is used to set sensorReading.
+
+@param
+ - sensorNumber
+ - operation
+ - reading
+ - assertOffset0_7
+ - assertOffset8_14
+ - deassertOffset0_7
+ - deassertOffset8_14
+ - eventData1
+ - eventData2
+ - eventData3
+
+@return completion code on success.
+**/
+
+ipmi::RspType<> ipmiSetSensorReading(uint8_t sensorNumber, uint8_t operation,
+ uint8_t reading, uint8_t assertOffset0_7,
+ uint8_t assertOffset8_14,
+ uint8_t deassertOffset0_7,
+ uint8_t deassertOffset8_14,
+ uint8_t eventData1, uint8_t eventData2,
+ uint8_t eventData3)
{
- ipmi::sensor::SetSensorReadingReq cmdData =
- *(static_cast<ipmi::sensor::SetSensorReadingReq*>(request));
+ log<level::DEBUG>("IPMI SET_SENSOR",
+ entry("SENSOR_NUM=0x%02x", sensorNumber));
+
+ ipmi::sensor::SetSensorReadingReq cmdData;
+
+ cmdData.number = sensorNumber;
+ cmdData.operation = operation;
+ cmdData.reading = reading;
+ cmdData.assertOffset0_7 = assertOffset0_7;
+ cmdData.assertOffset8_14 = assertOffset8_14;
+ cmdData.deassertOffset0_7 = deassertOffset0_7;
+ cmdData.deassertOffset8_14 = deassertOffset8_14;
+ cmdData.eventData1 = eventData1;
+ cmdData.eventData2 = eventData2;
+ cmdData.eventData3 = eventData3;
// Check if the Sensor Number is present
- const auto iter = sensors.find(cmdData.number);
- if (iter == sensors.end())
+ const auto iter = ipmi::sensor::sensors.find(sensorNumber);
+ if (iter == ipmi::sensor::sensors.end())
{
- return IPMI_CC_SENSOR_INVALID;
+ updateSensorRecordFromSSRAESC(&sensorNumber);
+ return ipmi::responseSuccess();
}
try
@@ -342,92 +381,104 @@ ipmi_ret_t setSensorReading(void* request)
(iter->second.mutability & ipmi::sensor::Mutability::Write))
{
log<level::ERR>("Sensor Set operation is not allowed",
- entry("SENSOR_NUM=%d", cmdData.number));
- return IPMI_CC_ILLEGAL_COMMAND;
+ entry("SENSOR_NUM=%d", sensorNumber));
+ return ipmi::responseIllegalCommand();
}
- return iter->second.updateFunc(cmdData, iter->second);
+ auto ipmiRC = iter->second.updateFunc(cmdData, iter->second);
+ return ipmi::response(ipmiRC);
}
catch (InternalFailure& e)
{
log<level::ERR>("Set sensor failed",
- entry("SENSOR_NUM=%d", cmdData.number));
+ entry("SENSOR_NUM=%d", sensorNumber));
commit<InternalFailure>();
+ return ipmi::responseUnspecifiedError();
}
catch (const std::runtime_error& e)
{
log<level::ERR>(e.what());
+ return ipmi::responseUnspecifiedError();
}
-
- return IPMI_CC_UNSPECIFIED_ERROR;
}
-ipmi_ret_t ipmi_sen_set_sensor(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request, ipmi_response_t response,
- ipmi_data_len_t data_len, ipmi_context_t context)
+/** @brief implements the get sensor reading command
+ * @param sensorNum - sensor number
+ *
+ * @returns IPMI completion code plus response data
+ * - senReading - sensor reading
+ * - reserved
+ * - readState - sensor reading state enabled
+ * - senScanState - sensor scan state disabled
+ * - allEventMessageState - all Event message state disabled
+ * - assertionStatesLsb - threshold levels states
+ * - assertionStatesMsb - discrete reading sensor states
+ */
+ipmi::RspType<uint8_t, // sensor reading
+
+ uint5_t, // reserved
+ bool, // reading state
+ bool, // 0 = sensor scanning state disabled
+ bool, // 0 = all event messages disabled
+
+ uint8_t, // threshold levels states
+ uint8_t // discrete reading sensor states
+ >
+ ipmiSensorGetSensorReading(uint8_t sensorNum)
{
- auto reqptr = static_cast<sensor_data_t*>(request);
-
- log<level::DEBUG>("IPMI SET_SENSOR",
- entry("SENSOR_NUM=0x%02x", reqptr->sennum));
-
- /*
- * This would support the Set Sensor Reading command for the presence
- * and functional state of Processor, Core & DIMM. For the remaining
- * sensors the existing support is invoked.
- */
- auto ipmiRC = setSensorReading(request);
-
- if (ipmiRC == IPMI_CC_SENSOR_INVALID)
+ if (sensorNum == 0xFF)
{
- updateSensorRecordFromSSRAESC(reqptr);
- ipmiRC = IPMI_CC_OK;
+ return ipmi::responseInvalidFieldRequest();
}
- *data_len = 0;
- return ipmiRC;
-}
-
-ipmi_ret_t ipmi_sen_get_sensor_reading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
-{
- auto reqptr = static_cast<sensor_data_t*>(request);
- auto resp = static_cast<sensorreadingresp_t*>(response);
- ipmi::sensor::GetSensorResponse getResponse{};
- static constexpr auto scanningEnabledBit = 6;
-
- const auto iter = sensors.find(reqptr->sennum);
- if (iter == sensors.end())
+ const auto iter = ipmi::sensor::sensors.find(sensorNum);
+ if (iter == ipmi::sensor::sensors.end())
{
- return IPMI_CC_SENSOR_INVALID;
+ return ipmi::responseSensorInvalid();
}
if (ipmi::sensor::Mutability::Read !=
(iter->second.mutability & ipmi::sensor::Mutability::Read))
{
- return IPMI_CC_ILLEGAL_COMMAND;
+ return ipmi::responseIllegalCommand();
}
try
{
- getResponse = iter->second.getFunc(iter->second);
- *data_len = getResponse.size();
- std::memcpy(resp, getResponse.data(), *data_len);
- resp->operation = 1 << scanningEnabledBit;
- return IPMI_CC_OK;
+ ipmi::sensor::GetSensorResponse getResponse =
+ iter->second.getFunc(iter->second);
+
+ return ipmi::responseSuccess(getResponse.reading, uint5_t(0),
+ getResponse.readingOrStateUnavailable,
+ getResponse.scanningEnabled,
+ getResponse.allEventMessagesEnabled,
+ getResponse.thresholdLevelsStates,
+ getResponse.discreteReadingSensorStates);
+ }
+#ifdef UPDATE_FUNCTIONAL_ON_FAIL
+ catch (const SensorFunctionalError& e)
+ {
+ return ipmi::responseResponseError();
}
+#endif
catch (const std::exception& e)
{
- *data_len = getResponse.size();
- std::memcpy(resp, getResponse.data(), *data_len);
- return IPMI_CC_OK;
+ // Intitilizing with default values
+ constexpr uint8_t senReading = 0;
+ constexpr uint5_t reserved{0};
+ constexpr bool readState = true;
+ constexpr bool senScanState = false;
+ constexpr bool allEventMessageState = false;
+ constexpr uint8_t assertionStatesLsb = 0;
+ constexpr uint8_t assertionStatesMsb = 0;
+
+ return ipmi::responseSuccess(senReading, reserved, readState,
+ senScanState, allEventMessageState,
+ assertionStatesLsb, assertionStatesMsb);
}
}
-void getSensorThresholds(uint8_t sensorNum,
- get_sdr::GetSensorThresholdsResponse* response)
+get_sdr::GetSensorThresholdsResponse getSensorThresholds(uint8_t sensorNum)
{
+ get_sdr::GetSensorThresholdsResponse resp;
constexpr auto warningThreshIntf =
"xyz.openbmc_project.Sensor.Threshold.Warning";
constexpr auto criticalThreshIntf =
@@ -435,7 +486,7 @@ void getSensorThresholds(uint8_t sensorNum,
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
- const auto iter = sensors.find(sensorNum);
+ const auto iter = ipmi::sensor::sensors.find(sensorNum);
const auto info = iter->second;
auto service = ipmi::getService(bus, info.sensorInterface, info.sensorPath);
@@ -443,76 +494,85 @@ void getSensorThresholds(uint8_t sensorNum,
auto warnThresholds = ipmi::getAllDbusProperties(
bus, service, info.sensorPath, warningThreshIntf);
- double warnLow = variant_ns::visit(ipmi::VariantToDoubleVisitor(),
- warnThresholds["WarningLow"]);
- double warnHigh = variant_ns::visit(ipmi::VariantToDoubleVisitor(),
- warnThresholds["WarningHigh"]);
+ double warnLow = std::visit(ipmi::VariantToDoubleVisitor(),
+ warnThresholds["WarningLow"]);
+ double warnHigh = std::visit(ipmi::VariantToDoubleVisitor(),
+ warnThresholds["WarningHigh"]);
if (warnLow != 0)
{
warnLow *= std::pow(10, info.scale - info.exponentR);
- response->lowerNonCritical = static_cast<uint8_t>(
+ resp.lowerNonCritical = static_cast<uint8_t>(
(warnLow - info.scaledOffset) / info.coefficientM);
- response->validMask |= static_cast<uint8_t>(
+ resp.validMask |= static_cast<uint8_t>(
ipmi::sensor::ThresholdMask::NON_CRITICAL_LOW_MASK);
}
if (warnHigh != 0)
{
warnHigh *= std::pow(10, info.scale - info.exponentR);
- response->upperNonCritical = static_cast<uint8_t>(
+ resp.upperNonCritical = static_cast<uint8_t>(
(warnHigh - info.scaledOffset) / info.coefficientM);
- response->validMask |= static_cast<uint8_t>(
+ resp.validMask |= static_cast<uint8_t>(
ipmi::sensor::ThresholdMask::NON_CRITICAL_HIGH_MASK);
}
auto critThresholds = ipmi::getAllDbusProperties(
bus, service, info.sensorPath, criticalThreshIntf);
- double critLow = variant_ns::visit(ipmi::VariantToDoubleVisitor(),
- critThresholds["CriticalLow"]);
- double critHigh = variant_ns::visit(ipmi::VariantToDoubleVisitor(),
- critThresholds["CriticalHigh"]);
+ double critLow = std::visit(ipmi::VariantToDoubleVisitor(),
+ critThresholds["CriticalLow"]);
+ double critHigh = std::visit(ipmi::VariantToDoubleVisitor(),
+ critThresholds["CriticalHigh"]);
if (critLow != 0)
{
critLow *= std::pow(10, info.scale - info.exponentR);
- response->lowerCritical = static_cast<uint8_t>(
+ resp.lowerCritical = static_cast<uint8_t>(
(critLow - info.scaledOffset) / info.coefficientM);
- response->validMask |= static_cast<uint8_t>(
+ resp.validMask |= static_cast<uint8_t>(
ipmi::sensor::ThresholdMask::CRITICAL_LOW_MASK);
}
if (critHigh != 0)
{
critHigh *= std::pow(10, info.scale - info.exponentR);
- response->upperCritical = static_cast<uint8_t>(
+ resp.upperCritical = static_cast<uint8_t>(
(critHigh - info.scaledOffset) / info.coefficientM);
- response->validMask |= static_cast<uint8_t>(
+ resp.validMask |= static_cast<uint8_t>(
ipmi::sensor::ThresholdMask::CRITICAL_HIGH_MASK);
}
+
+ return resp;
}
-ipmi_ret_t ipmi_sen_get_sensor_thresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief implements the get sensor thresholds command
+ * @param sensorNum - sensor number
+ *
+ * @returns IPMI completion code plus response data
+ * - validMask - threshold mask
+ * - lower non-critical threshold - IPMI messaging state
+ * - lower critical threshold - link authentication state
+ * - lower non-recoverable threshold - callback state
+ * - upper non-critical threshold
+ * - upper critical
+ * - upper non-recoverable
+ */
+ipmi::RspType<uint8_t, // validMask
+ uint8_t, // lowerNonCritical
+ uint8_t, // lowerCritical
+ uint8_t, // lowerNonRecoverable
+ uint8_t, // upperNonCritical
+ uint8_t, // upperCritical
+ uint8_t // upperNonRecoverable
+ >
+ ipmiSensorGetSensorThresholds(uint8_t sensorNum)
{
constexpr auto valueInterface = "xyz.openbmc_project.Sensor.Value";
- if (*data_len != sizeof(uint8_t))
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- auto sensorNum = *(reinterpret_cast<const uint8_t*>(request));
- *data_len = 0;
-
- const auto iter = sensors.find(sensorNum);
- if (iter == sensors.end())
+ const auto iter = ipmi::sensor::sensors.find(sensorNum);
+ if (iter == ipmi::sensor::sensors.end())
{
- return IPMI_CC_SENSOR_INVALID;
+ return ipmi::responseSensorInvalid();
}
const auto info = iter->second;
@@ -522,82 +582,75 @@ ipmi_ret_t ipmi_sen_get_sensor_thresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
info.propertyInterfaces.end())
{
// return with valid mask as 0
- return IPMI_CC_OK;
+ return ipmi::responseSuccess();
}
- auto responseData =
- reinterpret_cast<get_sdr::GetSensorThresholdsResponse*>(response);
-
+ get_sdr::GetSensorThresholdsResponse resp{};
try
{
- getSensorThresholds(sensorNum, responseData);
+ resp = getSensorThresholds(sensorNum);
}
catch (std::exception& e)
{
// Mask if the property is not present
- responseData->validMask = 0;
}
- *data_len = sizeof(get_sdr::GetSensorThresholdsResponse);
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(resp.validMask, resp.lowerNonCritical,
+ resp.lowerCritical, resp.lowerNonRecoverable,
+ resp.upperNonCritical, resp.upperCritical,
+ resp.upperNonRecoverable);
}
-ipmi_ret_t ipmi_sen_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request, ipmi_response_t response,
- ipmi_data_len_t data_len, ipmi_context_t context)
+/** @brief implements the get SDR Info command
+ * @param count - Operation
+ *
+ * @returns IPMI completion code plus response data
+ * - sdrCount - sensor/SDR count
+ * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
+ */
+ipmi::RspType<uint8_t, // respcount
+ uint8_t // dynamic population flags
+ >
+ ipmiSensorGetDeviceSdrInfo(std::optional<uint8_t> count)
{
- ipmi_ret_t rc = IPMI_CC_INVALID;
-
- printf("IPMI S/E Wildcard Netfn:[0x%X], Cmd:[0x%X]\n", netfn, cmd);
- *data_len = 0;
-
- return rc;
-}
+ uint8_t sdrCount;
+ // multiple LUNs not supported.
+ constexpr uint8_t lunsAndDynamicPopulation = 1;
+ constexpr uint8_t getSdrCount = 0x01;
+ constexpr uint8_t getSensorCount = 0x00;
-ipmi_ret_t ipmi_sen_get_sdr_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
-{
- auto resp = static_cast<get_sdr_info::GetSdrInfoResp*>(response);
- if (request == nullptr ||
- get_sdr_info::request::get_count(request) == false)
+ if (count.value_or(0) == getSdrCount)
{
- // Get Sensor Count
- resp->count = sensors.size() + frus.size() + entities.size();
+ // Get SDR count. This returns the total number of SDRs in the device.
+ const auto& entityRecords =
+ ipmi::sensor::EntityInfoMapContainer::getContainer()
+ ->getIpmiEntityRecords();
+ sdrCount =
+ ipmi::sensor::sensors.size() + frus.size() + entityRecords.size();
+ }
+ else if (count.value_or(0) == getSensorCount)
+ {
+ // Get Sensor count. This returns the number of sensors
+ sdrCount = ipmi::sensor::sensors.size();
}
else
{
- resp->count = 1;
+ return ipmi::responseInvalidCommandOnLun();
}
- // Multiple LUNs not supported.
- namespace response = get_sdr_info::response;
- response::set_lun_present(0, &(resp->luns_and_dynamic_population));
- response::set_lun_not_present(1, &(resp->luns_and_dynamic_population));
- response::set_lun_not_present(2, &(resp->luns_and_dynamic_population));
- response::set_lun_not_present(3, &(resp->luns_and_dynamic_population));
- response::set_static_population(&(resp->luns_and_dynamic_population));
-
- *data_len = SDR_INFO_RESP_SIZE;
-
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation);
}
-ipmi_ret_t ipmi_sen_reserve_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief implements the reserve SDR command
+ * @returns IPMI completion code plus response data
+ * - reservationID - reservation ID
+ */
+ipmi::RspType<uint16_t> ipmiSensorReserveSdr()
{
// A constant reservation ID is okay until we implement add/remove SDR.
- const uint16_t reservation_id = 1;
- *(uint16_t*)response = reservation_id;
- *data_len = sizeof(uint16_t);
+ constexpr uint16_t reservationID = 1;
- printf("Created new IPMI SDR reservation ID %d\n", *(uint16_t*)response);
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(reservationID);
}
void setUnitFieldsForObject(const ipmi::sensor::Info* info,
@@ -740,9 +793,13 @@ ipmi_ret_t ipmi_fru_get_sdr(ipmi_request_t request, ipmi_response_t response,
{
// we have reached till end of fru, so assign the next record id to
// 512(Max fru ID = 511) + Entity Record ID(may start with 0).
+ const auto& entityRecords =
+ ipmi::sensor::EntityInfoMapContainer::getContainer()
+ ->getIpmiEntityRecords();
auto next_record_id =
- (entities.size()) ? entities.begin()->first + ENTITY_RECORD_ID_START
- : END_OF_RECORD;
+ (entityRecords.size())
+ ? entityRecords.begin()->first + ENTITY_RECORD_ID_START
+ : END_OF_RECORD;
get_sdr::response::set_next_record_id(next_record_id, resp);
}
else
@@ -777,13 +834,16 @@ ipmi_ret_t ipmi_entity_get_sdr(ipmi_request_t request, ipmi_response_t response,
get_sdr::SensorDataEntityRecord record{};
auto dataLength = 0;
- auto entity = entities.begin();
+ const auto& entityRecords =
+ ipmi::sensor::EntityInfoMapContainer::getContainer()
+ ->getIpmiEntityRecords();
+ auto entity = entityRecords.begin();
uint8_t entityRecordID;
auto recordID = get_sdr::request::get_record_id(req);
entityRecordID = recordID - ENTITY_RECORD_ID_START;
- entity = entities.find(entityRecordID);
- if (entity == entities.end())
+ entity = entityRecords.find(entityRecordID);
+ if (entity == entityRecords.end())
{
return IPMI_CC_SENSOR_INVALID;
}
@@ -810,7 +870,7 @@ ipmi_ret_t ipmi_entity_get_sdr(ipmi_request_t request, ipmi_response_t response,
record.body.entityId4 = entity->second.containedEntities[3].first;
record.body.entityInstance4 = entity->second.containedEntities[3].second;
- if (++entity == entities.end())
+ if (++entity == entityRecords.end())
{
get_sdr::response::set_next_record_id(END_OF_RECORD,
resp); // last record
@@ -847,101 +907,98 @@ ipmi_ret_t ipmi_sen_get_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
get_sdr::GetSdrReq* req = (get_sdr::GetSdrReq*)request;
get_sdr::GetSdrResp* resp = (get_sdr::GetSdrResp*)response;
get_sdr::SensorDataFullRecord record = {0};
- if (req != NULL)
- {
- // Note: we use an iterator so we can provide the next ID at the end of
- // the call.
- auto sensor = sensors.begin();
- auto recordID = get_sdr::request::get_record_id(req);
- // At the beginning of a scan, the host side will send us id=0.
- if (recordID != 0)
- {
- // recordID 0 to 255 means it is a FULL record.
- // recordID 256 to 511 means it is a FRU record.
- // recordID greater then 511 means it is a Entity Association
- // record. Currently we are supporting three record types: FULL
- // record, FRU record and Enttiy Association record.
- if (recordID >= ENTITY_RECORD_ID_START)
- {
- return ipmi_entity_get_sdr(request, response, data_len);
- }
- else if (recordID >= FRU_RECORD_ID_START &&
- recordID < ENTITY_RECORD_ID_START)
- {
- return ipmi_fru_get_sdr(request, response, data_len);
- }
- else
- {
- sensor = sensors.find(recordID);
- if (sensor == sensors.end())
- {
- return IPMI_CC_SENSOR_INVALID;
- }
- }
- }
+ // Note: we use an iterator so we can provide the next ID at the end of
+ // the call.
+ auto sensor = ipmi::sensor::sensors.begin();
+ auto recordID = get_sdr::request::get_record_id(req);
- uint8_t sensor_id = sensor->first;
-
- /* Header */
- get_sdr::header::set_record_id(sensor_id, &(record.header));
- record.header.sdr_version = 0x51; // Based on IPMI Spec v2.0 rev 1.1
- record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
- record.header.record_length = sizeof(get_sdr::SensorDataFullRecord);
-
- /* Key */
- get_sdr::key::set_owner_id_bmc(&(record.key));
- record.key.sensor_number = sensor_id;
-
- /* Body */
- record.body.entity_id = sensor->second.entityType;
- record.body.sensor_type = sensor->second.sensorType;
- record.body.event_reading_type = sensor->second.sensorReadingType;
- record.body.entity_instance = sensor->second.instance;
- if (ipmi::sensor::Mutability::Write ==
- (sensor->second.mutability & ipmi::sensor::Mutability::Write))
+ // At the beginning of a scan, the host side will send us id=0.
+ if (recordID != 0)
+ {
+ // recordID 0 to 255 means it is a FULL record.
+ // recordID 256 to 511 means it is a FRU record.
+ // recordID greater then 511 means it is a Entity Association
+ // record. Currently we are supporting three record types: FULL
+ // record, FRU record and Enttiy Association record.
+ if (recordID >= ENTITY_RECORD_ID_START)
{
- get_sdr::body::init_settable_state(true, &(record.body));
+ return ipmi_entity_get_sdr(request, response, data_len);
}
-
- // Set the type-specific details given the DBus interface
- ret = populate_record_from_dbus(&(record.body), &(sensor->second),
- data_len);
-
- if (++sensor == sensors.end())
+ else if (recordID >= FRU_RECORD_ID_START &&
+ recordID < ENTITY_RECORD_ID_START)
{
- // we have reached till end of sensor, so assign the next record id
- // to 256(Max Sensor ID = 255) + FRU ID(may start with 0).
- auto next_record_id =
- (frus.size()) ? frus.begin()->first + FRU_RECORD_ID_START
- : END_OF_RECORD;
-
- get_sdr::response::set_next_record_id(next_record_id, resp);
+ return ipmi_fru_get_sdr(request, response, data_len);
}
else
{
- get_sdr::response::set_next_record_id(sensor->first, resp);
+ sensor = ipmi::sensor::sensors.find(recordID);
+ if (sensor == ipmi::sensor::sensors.end())
+ {
+ return IPMI_CC_SENSOR_INVALID;
+ }
}
+ }
- if (req->offset > sizeof(record))
- {
- return IPMI_CC_PARM_OUT_OF_RANGE;
- }
+ uint8_t sensor_id = sensor->first;
+
+ /* Header */
+ get_sdr::header::set_record_id(sensor_id, &(record.header));
+ record.header.sdr_version = 0x51; // Based on IPMI Spec v2.0 rev 1.1
+ record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
+ record.header.record_length = sizeof(get_sdr::SensorDataFullRecord);
+
+ /* Key */
+ get_sdr::key::set_owner_id_bmc(&(record.key));
+ record.key.sensor_number = sensor_id;
+
+ /* Body */
+ record.body.entity_id = sensor->second.entityType;
+ record.body.sensor_type = sensor->second.sensorType;
+ record.body.event_reading_type = sensor->second.sensorReadingType;
+ record.body.entity_instance = sensor->second.instance;
+ if (ipmi::sensor::Mutability::Write ==
+ (sensor->second.mutability & ipmi::sensor::Mutability::Write))
+ {
+ get_sdr::body::init_settable_state(true, &(record.body));
+ }
- // data_len will ultimately be the size of the record, plus
- // the size of the next record ID:
- *data_len = std::min(static_cast<size_t>(req->bytes_to_read),
- sizeof(record) - req->offset);
+ // Set the type-specific details given the DBus interface
+ ret =
+ populate_record_from_dbus(&(record.body), &(sensor->second), data_len);
- std::memcpy(resp->record_data,
- reinterpret_cast<uint8_t*>(&record) + req->offset,
- *data_len);
+ if (++sensor == ipmi::sensor::sensors.end())
+ {
+ // we have reached till end of sensor, so assign the next record id
+ // to 256(Max Sensor ID = 255) + FRU ID(may start with 0).
+ auto next_record_id = (frus.size())
+ ? frus.begin()->first + FRU_RECORD_ID_START
+ : END_OF_RECORD;
- // data_len should include the LSB and MSB:
- *data_len +=
- sizeof(resp->next_record_id_lsb) + sizeof(resp->next_record_id_msb);
+ get_sdr::response::set_next_record_id(next_record_id, resp);
+ }
+ else
+ {
+ get_sdr::response::set_next_record_id(sensor->first, resp);
}
+ if (req->offset > sizeof(record))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ // data_len will ultimately be the size of the record, plus
+ // the size of the next record ID:
+ *data_len = std::min(static_cast<size_t>(req->bytes_to_read),
+ sizeof(record) - req->offset);
+
+ std::memcpy(resp->record_data,
+ reinterpret_cast<uint8_t*>(&record) + req->offset, *data_len);
+
+ // data_len should include the LSB and MSB:
+ *data_len +=
+ sizeof(resp->next_record_id_lsb) + sizeof(resp->next_record_id_msb);
+
return ret;
}
@@ -1037,10 +1094,6 @@ ipmi_ret_t ipmicmdPlatformEvent(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
void register_netfn_sen_functions()
{
- // <Wildcard Command>
- ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
- ipmi_sen_wildcard, PRIVILEGE_USER);
-
// <Platform Event Message>
ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_PLATFORM_EVENT, nullptr,
ipmicmdPlatformEvent, PRIVILEGE_OPERATOR);
@@ -1049,29 +1102,32 @@ void register_netfn_sen_functions()
ipmi_sen_get_sensor_type, PRIVILEGE_USER);
// <Set Sensor Reading and Event Status>
- ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_SET_SENSOR, nullptr,
- ipmi_sen_set_sensor, PRIVILEGE_OPERATOR);
-
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
+ ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
+ ipmi::Privilege::Operator, ipmiSetSensorReading);
// <Get Sensor Reading>
- ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_READING, nullptr,
- ipmi_sen_get_sensor_reading, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
+ ipmi::sensor_event::cmdGetSensorReading,
+ ipmi::Privilege::User, ipmiSensorGetSensorReading);
// <Reserve Device SDR Repository>
- ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_RESERVE_DEVICE_SDR_REPO,
- nullptr, ipmi_sen_reserve_sdr, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
+ ipmi::sensor_event::cmdReserveDeviceSdrRepository,
+ ipmi::Privilege::User, ipmiSensorReserveSdr);
// <Get Device SDR Info>
- ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_DEVICE_SDR_INFO, nullptr,
- ipmi_sen_get_sdr_info, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
+ ipmi::sensor_event::cmdGetDeviceSdrInfo,
+ ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
// <Get Device SDR>
ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_DEVICE_SDR, nullptr,
ipmi_sen_get_sdr, PRIVILEGE_USER);
// <Get Sensor Thresholds>
- ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_THRESHOLDS,
- nullptr, ipmi_sen_get_sensor_thresholds,
- PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
+ ipmi::sensor_event::cmdGetSensorThreshold,
+ ipmi::Privilege::User, ipmiSensorGetSensorThresholds);
return;
}
diff --git a/sensorhandler.hpp b/sensorhandler.hpp
index 954a3ed..8780235 100644
--- a/sensorhandler.hpp
+++ b/sensorhandler.hpp
@@ -2,6 +2,7 @@
#include <stdint.h>
+#include <exception>
#include <ipmid/api.hpp>
#include <ipmid/types.hpp>
@@ -38,6 +39,16 @@ enum ipmi_sensor_types
IPMI_SENSOR_TPM = 0xCC,
};
+/** @brief Custom exception for reading sensors that are not funcitonal.
+ */
+struct SensorFunctionalError : public std::exception
+{
+ const char* what() const noexcept
+ {
+ return "Sensor not functional";
+ }
+};
+
#define MAX_DBUS_PATH 128
struct dbus_interface_t
{
@@ -79,11 +90,7 @@ ipmi_ret_t ipmi_sen_get_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request, ipmi_response_t response,
ipmi_data_len_t data_len, ipmi_context_t context);
-ipmi_ret_t ipmi_sen_reserve_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context);
+ipmi::RspType<uint16_t> ipmiSensorReserveSdr();
static const uint16_t FRU_RECORD_ID_START = 256;
static const uint16_t ENTITY_RECORD_ID_START = 512;
@@ -106,34 +113,6 @@ inline bool get_count(void* req)
return (bool)((uint64_t)(req)&1);
}
} // namespace request
-
-namespace response
-{
-#define SDR_INFO_RESP_SIZE 2
-inline void set_lun_present(int lun, uint8_t* resp)
-{
- *resp |= 1 << lun;
-}
-inline void set_lun_not_present(int lun, uint8_t* resp)
-{
- *resp &= ~(1 << lun);
-}
-inline void set_dynamic_population(uint8_t* resp)
-{
- *resp |= 1 << 7;
-}
-inline void set_static_population(uint8_t* resp)
-{
- *resp &= ~(1 << 7);
-}
-} // namespace response
-
-struct GetSdrInfoResp
-{
- uint8_t count;
- uint8_t luns_and_dynamic_population;
-};
-
} // namespace get_sdr_info
/**
@@ -155,7 +134,7 @@ struct GetSdrReq
namespace request
{
-inline uint8_t get_reservation_id(GetSdrReq* req)
+inline uint16_t get_reservation_id(GetSdrReq* req)
{
return (req->reservation_id_lsb + (req->reservation_id_msb << 8));
};
@@ -665,15 +644,15 @@ namespace sensor
* @param[in] offset - offset number.
* @param[in/out] resp - get sensor reading response.
*/
-inline void setOffset(uint8_t offset, ipmi::sensor::GetReadingResponse* resp)
+inline void setOffset(uint8_t offset, ipmi::sensor::GetSensorResponse* resp)
{
if (offset > 7)
{
- resp->assertOffset8_14 |= 1 << (offset - 8);
+ resp->discreteReadingSensorStates |= 1 << (offset - 8);
}
else
{
- resp->assertOffset0_7 |= 1 << offset;
+ resp->thresholdLevelsStates |= 1 << offset;
}
}
@@ -683,7 +662,7 @@ inline void setOffset(uint8_t offset, ipmi::sensor::GetReadingResponse* resp)
* @param[in] offset - offset number.
* @param[in/out] resp - get sensor reading response.
*/
-inline void setReading(uint8_t value, ipmi::sensor::GetReadingResponse* resp)
+inline void setReading(uint8_t value, ipmi::sensor::GetSensorResponse* resp)
{
resp->reading = value;
}
@@ -696,10 +675,10 @@ inline void setReading(uint8_t value, ipmi::sensor::GetReadingResponse* resp)
* @param[in/out] resp - get sensor reading response.
*/
inline void setAssertionBytes(uint16_t value,
- ipmi::sensor::GetReadingResponse* resp)
+ ipmi::sensor::GetSensorResponse* resp)
{
- resp->assertOffset0_7 = static_cast<uint8_t>(value & 0x00FF);
- resp->assertOffset8_14 = static_cast<uint8_t>(value >> 8);
+ resp->thresholdLevelsStates = static_cast<uint8_t>(value & 0x00FF);
+ resp->discreteReadingSensorStates = static_cast<uint8_t>(value >> 8);
}
/**
@@ -707,9 +686,11 @@ inline void setAssertionBytes(uint16_t value,
*
* @param[in/out] resp - get sensor reading response.
*/
-inline void enableScanning(ipmi::sensor::GetReadingResponse* resp)
+inline void enableScanning(ipmi::sensor::GetSensorResponse* resp)
{
- resp->operation = 1 << 6;
+ resp->readingOrStateUnavailable = false;
+ resp->scanningEnabled = true;
+ resp->allEventMessagesEnabled = false;
}
} // namespace sensor
diff --git a/settings.cpp b/settings.cpp
index e8b8997..2fa2511 100644
--- a/settings.cpp
+++ b/settings.cpp
@@ -11,7 +11,6 @@ namespace settings
using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
-namespace variant_ns = sdbusplus::message::variant_ns;
constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
constexpr auto mapperPath = "/xyz/openbmc_project/object_mapper";
@@ -130,9 +129,9 @@ std::tuple<Path, OneTimeEnabled> setting(const Objects& objects,
elog<InternalFailure>();
}
- sdbusplus::message::variant<bool> enabled;
+ std::variant<bool> enabled;
reply.read(enabled);
- auto oneTimeEnabled = variant_ns::get<bool>(enabled);
+ auto oneTimeEnabled = std::get<bool>(enabled);
const Path& setting = oneTimeEnabled ? oneTimeSetting : regularSetting;
return std::make_tuple(setting, oneTimeEnabled);
}
diff --git a/storagehandler.cpp b/storagehandler.cpp
index 18f1357..5b9726a 100644
--- a/storagehandler.cpp
+++ b/storagehandler.cpp
@@ -19,17 +19,24 @@
#include <ipmid/utils.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
-#include <sdbusplus/message/types.hpp>
#include <sdbusplus/server.hpp>
#include <string>
+#include <variant>
#include <xyz/openbmc_project/Common/error.hpp>
void register_netfn_storage_functions() __attribute__((constructor));
unsigned int g_sel_time = 0xFFFFFFFF;
-extern const ipmi::sensor::IdInfoMap sensors;
-extern const FruMap frus;
+namespace ipmi
+{
+namespace sensor
+{
+extern const IdInfoMap sensors;
+} // namespace sensor
+} // namespace ipmi
+extern const FruMap frus;
+constexpr uint8_t eventDataSize = 3;
namespace
{
constexpr auto TIME_INTERFACE = "xyz.openbmc_project.Time.EpochTime";
@@ -37,13 +44,6 @@ constexpr auto HOST_TIME_PATH = "/xyz/openbmc_project/time/host";
constexpr auto DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
constexpr auto PROPERTY_ELAPSED = "Elapsed";
-const char* getTimeString(const uint64_t& usecSinceEpoch)
-{
- using namespace std::chrono;
- system_clock::time_point tp{microseconds(usecSinceEpoch)};
- auto t = system_clock::to_time_t(tp);
- return std::ctime(&t);
-}
} // namespace
namespace cache
@@ -62,8 +62,6 @@ ipmi::sel::ObjectPaths paths;
} // namespace cache
-namespace variant_ns = sdbusplus::message::variant_ns;
-
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
using namespace phosphor::logging;
@@ -78,36 +76,34 @@ enum class AccessMode
words ///< Device is accessed by words
};
-ipmi_ret_t ipmi_storage_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
-{
- // Status code.
- ipmi_ret_t rc = IPMI_CC_INVALID;
- *data_len = 0;
- return rc;
-}
+/** @brief implements the get SEL Info command
+ * @returns IPMI completion code plus response data
+ * - selVersion - SEL revision
+ * - entries - Number of log entries in SEL.
+ * - freeSpace - Free Space in bytes.
+ * - addTimeStamp - Most recent addition timestamp
+ * - eraseTimeStamp - Most recent erase timestamp
+ * - operationSupport - Reserve & Delete SEL operations supported
+ */
-ipmi_ret_t getSELInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request, ipmi_response_t response,
- ipmi_data_len_t data_len, ipmi_context_t context)
+ipmi::RspType<uint8_t, // SEL revision.
+ uint16_t, // number of log entries in SEL.
+ uint16_t, // free Space in bytes.
+ uint32_t, // most recent addition timestamp
+ uint32_t, // most recent erase timestamp.
+
+ bool, // SEL allocation info supported
+ bool, // reserve SEL supported
+ bool, // partial Add SEL Entry supported
+ bool, // delete SEL supported
+ uint3_t, // reserved
+ bool // overflow flag
+ >
+ ipmiStorageGetSelInfo()
{
- if (*data_len != 0)
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- std::vector<uint8_t> outPayload(sizeof(ipmi::sel::GetSELInfoResponse));
- auto responseData =
- reinterpret_cast<ipmi::sel::GetSELInfoResponse*>(outPayload.data());
-
- responseData->selVersion = ipmi::sel::selVersion;
- // Last erase timestamp is not available from log manager.
- responseData->eraseTimeStamp = ipmi::sel::invalidTimeStamp;
- responseData->operationSupport = ipmi::sel::operationSupport;
+ uint16_t entries = 0;
+ // Most recent addition timestamp.
+ uint32_t addTimeStamp = ipmi::sel::invalidTimeStamp;
try
{
@@ -121,16 +117,13 @@ ipmi_ret_t getSELInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
// as 0.
}
- responseData->entries = 0;
- responseData->addTimeStamp = ipmi::sel::invalidTimeStamp;
-
if (!cache::paths.empty())
{
- responseData->entries = static_cast<uint16_t>(cache::paths.size());
+ entries = static_cast<uint16_t>(cache::paths.size());
try
{
- responseData->addTimeStamp = static_cast<uint32_t>(
+ addTimeStamp = static_cast<uint32_t>(
(ipmi::sel::getEntryTimeStamp(cache::paths.back()).count()));
}
catch (InternalFailure& e)
@@ -142,10 +135,18 @@ ipmi_ret_t getSELInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
}
}
- std::memcpy(response, outPayload.data(), outPayload.size());
- *data_len = outPayload.size();
+ constexpr uint8_t selVersion = ipmi::sel::selVersion;
+ constexpr uint16_t freeSpace = 0xFFFF;
+ constexpr uint32_t eraseTimeStamp = ipmi::sel::invalidTimeStamp;
+ constexpr uint3_t reserved{0};
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(
+ selVersion, entries, freeSpace, addTimeStamp, eraseTimeStamp,
+ ipmi::sel::operationSupport::getSelAllocationInfo,
+ ipmi::sel::operationSupport::reserveSel,
+ ipmi::sel::operationSupport::partialAddSelEntry,
+ ipmi::sel::operationSupport::deleteSel, reserved,
+ ipmi::sel::operationSupport::overflow);
}
ipmi_ret_t getSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
@@ -269,24 +270,24 @@ ipmi_ret_t getSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
return IPMI_CC_OK;
}
-ipmi_ret_t deleteSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request, ipmi_response_t response,
- ipmi_data_len_t data_len, ipmi_context_t context)
+/** @brief implements the delete SEL entry command
+ * @request
+ * - reservationID; // reservation ID.
+ * - selRecordID; // SEL record ID.
+ *
+ * @returns ipmi completion code plus response data
+ * - Record ID of the deleted record
+ */
+ipmi::RspType<uint16_t // deleted record ID
+ >
+ deleteSELEntry(uint16_t reservationID, uint16_t selRecordID)
{
- if (*data_len != sizeof(ipmi::sel::DeleteSELEntryRequest))
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
namespace fs = std::filesystem;
- auto requestData =
- reinterpret_cast<const ipmi::sel::DeleteSELEntryRequest*>(request);
- if (!checkSELReservation(requestData->reservationID))
+ if (!checkSELReservation(reservationID))
{
- *data_len = 0;
- return IPMI_CC_INVALID_RESERVATION_ID;
+ return ipmi::responseInvalidReservationId();
}
// Per the IPMI spec, need to cancel the reservation when a SEL entry is
@@ -301,27 +302,25 @@ ipmi_ret_t deleteSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
{
// readLoggingObjectPaths will throw exception if there are no error
// log entries.
- *data_len = 0;
- return IPMI_CC_SENSOR_INVALID;
+ return ipmi::responseSensorInvalid();
}
if (cache::paths.empty())
{
- *data_len = 0;
- return IPMI_CC_SENSOR_INVALID;
+ return ipmi::responseSensorInvalid();
}
ipmi::sel::ObjectPaths::const_iterator iter;
uint16_t delRecordID = 0;
- if (requestData->selRecordID == ipmi::sel::firstEntry)
+ if (selRecordID == ipmi::sel::firstEntry)
{
iter = cache::paths.begin();
fs::path path(*iter);
delRecordID = static_cast<uint16_t>(
std::stoul(std::string(path.filename().c_str())));
}
- else if (requestData->selRecordID == ipmi::sel::lastEntry)
+ else if (selRecordID == ipmi::sel::lastEntry)
{
iter = cache::paths.end();
fs::path path(*iter);
@@ -331,15 +330,14 @@ ipmi_ret_t deleteSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
else
{
std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
- std::to_string(requestData->selRecordID);
+ std::to_string(selRecordID);
iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
if (iter == cache::paths.end())
{
- *data_len = 0;
- return IPMI_CC_SENSOR_INVALID;
+ return ipmi::responseSensorInvalid();
}
- delRecordID = requestData->selRecordID;
+ delRecordID = selRecordID;
}
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
@@ -352,8 +350,7 @@ ipmi_ret_t deleteSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
catch (const std::runtime_error& e)
{
log<level::ERR>(e.what());
- *data_len = 0;
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
auto methodCall = bus.new_method_call(service.c_str(), (*iter).c_str(),
@@ -361,55 +358,49 @@ ipmi_ret_t deleteSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
auto reply = bus.call(methodCall);
if (reply.is_method_error())
{
- *data_len = 0;
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
// Invalidate the cache of dbus entry objects.
cache::paths.clear();
- std::memcpy(response, &delRecordID, sizeof(delRecordID));
- *data_len = sizeof(delRecordID);
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(delRecordID);
}
-ipmi_ret_t clearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len,
- ipmi_context_t context)
-{
- if (*data_len != sizeof(ipmi::sel::ClearSELRequest))
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- auto requestData =
- reinterpret_cast<const ipmi::sel::ClearSELRequest*>(request);
+/** @brief implements the Clear SEL command
+ * @request
+ * - reservationID // Reservation ID.
+ * - clr // char array { 'C'(0x43h), 'L'(0x4Ch), 'R'(0x52h) }
+ * - eraseOperation; // requested operation.
+ *
+ * @returns ipmi completion code plus response data
+ * - erase status
+ */
- if (!checkSELReservation(requestData->reservationID))
+ipmi::RspType<uint8_t // erase status
+ >
+ clearSEL(uint16_t reservationID, const std::array<char, 3>& clr,
+ uint8_t eraseOperation)
+{
+ static constexpr std::array<char, 3> clrOk = {'C', 'L', 'R'};
+ if (clr != clrOk)
{
- *data_len = 0;
- return IPMI_CC_INVALID_RESERVATION_ID;
+ return ipmi::responseInvalidFieldRequest();
}
- if (requestData->charC != 'C' || requestData->charL != 'L' ||
- requestData->charR != 'R')
+ if (!checkSELReservation(reservationID))
{
- *data_len = 0;
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return ipmi::responseInvalidReservationId();
}
- uint8_t eraseProgress = ipmi::sel::eraseComplete;
-
/*
* Erasure status cannot be fetched from DBUS, so always return erasure
* status as `erase completed`.
*/
- if (requestData->eraseOperation == ipmi::sel::getEraseStatus)
+ if (eraseOperation == ipmi::sel::getEraseStatus)
{
- std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
- *data_len = sizeof(eraseProgress);
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(
+ static_cast<uint8_t>(ipmi::sel::eraseComplete));
}
// Per the IPMI spec, need to cancel any reservation when the SEL is cleared
@@ -431,24 +422,21 @@ ipmi_ret_t clearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
auto reply = bus.call(mapperCall);
if (reply.is_method_error())
{
- std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
- *data_len = sizeof(eraseProgress);
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(
+ static_cast<uint8_t>(ipmi::sel::eraseComplete));
}
reply.read(objectPaths);
if (objectPaths.empty())
{
- std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
- *data_len = sizeof(eraseProgress);
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(
+ static_cast<uint8_t>(ipmi::sel::eraseComplete));
}
}
catch (const sdbusplus::exception::SdBusError& e)
{
- std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
- *data_len = sizeof(eraseProgress);
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(
+ static_cast<uint8_t>(ipmi::sel::eraseComplete));
}
std::string service;
@@ -461,8 +449,7 @@ ipmi_ret_t clearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
catch (const std::runtime_error& e)
{
log<level::ERR>(e.what());
- *data_len = 0;
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
for (const auto& iter : objectPaths)
@@ -473,40 +460,32 @@ ipmi_ret_t clearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
auto reply = bus.call(methodCall);
if (reply.is_method_error())
{
- *data_len = 0;
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
}
// Invalidate the cache of dbus entry objects.
cache::paths.clear();
- std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
- *data_len = sizeof(eraseProgress);
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(
+ static_cast<uint8_t>(ipmi::sel::eraseComplete));
}
-ipmi_ret_t ipmi_storage_get_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief implements the get SEL time command
+ * @returns IPMI completion code plus response data
+ * -current time
+ */
+ipmi::RspType<uint32_t> // current time
+ ipmiStorageGetSelTime()
{
- if (*data_len != 0)
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
using namespace std::chrono;
uint64_t host_time_usec = 0;
- uint32_t resp = 0;
std::stringstream hostTime;
try
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
- sdbusplus::message::variant<uint64_t> value;
+ std::variant<uint64_t> value;
// Get host time
auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
@@ -519,64 +498,48 @@ ipmi_ret_t ipmi_storage_get_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
log<level::ERR>("Error getting time",
entry("SERVICE=%s", service.c_str()),
entry("PATH=%s", HOST_TIME_PATH));
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
reply.read(value);
- host_time_usec = variant_ns::get<uint64_t>(value);
+ host_time_usec = std::get<uint64_t>(value);
}
catch (InternalFailure& e)
{
log<level::ERR>(e.what());
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
- catch (const std::runtime_error& e)
+ catch (const std::exception& e)
{
log<level::ERR>(e.what());
- return IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
- hostTime << "Host time:" << getTimeString(host_time_usec);
+ hostTime << "Host time:"
+ << duration_cast<seconds>(microseconds(host_time_usec)).count();
log<level::DEBUG>(hostTime.str().c_str());
// Time is really long int but IPMI wants just uint32. This works okay until
// the number of seconds since 1970 overflows uint32 size.. Still a whole
// lot of time here to even think about that.
- resp = duration_cast<seconds>(microseconds(host_time_usec)).count();
- resp = htole32(resp);
-
- // From the IPMI Spec 2.0, response should be a 32-bit value
- *data_len = sizeof(resp);
-
- // Pack the actual response
- std::memcpy(response, &resp, *data_len);
-
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(
+ duration_cast<seconds>(microseconds(host_time_usec)).count());
}
-ipmi_ret_t ipmi_storage_set_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief implements the set SEL time command
+ * @param selDeviceTime - epoch time
+ * -local time as the number of seconds from 00:00:00, January 1, 1970
+ * @returns IPMI completion code
+ */
+ipmi::RspType<> ipmiStorageSetSelTime(uint32_t selDeviceTime)
{
- if (*data_len != sizeof(uint32_t))
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
using namespace std::chrono;
- ipmi_ret_t rc = IPMI_CC_OK;
- uint32_t secs = *static_cast<uint32_t*>(request);
- *data_len = 0;
-
- secs = le32toh(secs);
- microseconds usec{seconds(secs)};
+ microseconds usec{seconds(selDeviceTime)};
try
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
- sdbusplus::message::variant<uint64_t> value{usec.count()};
+ std::variant<uint64_t> value{usec.count()};
// Set host time
auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
@@ -589,257 +552,243 @@ ipmi_ret_t ipmi_storage_set_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
log<level::ERR>("Error setting time",
entry("SERVICE=%s", service.c_str()),
entry("PATH=%s", HOST_TIME_PATH));
- rc = IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
}
catch (InternalFailure& e)
{
log<level::ERR>(e.what());
- rc = IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
- catch (const std::runtime_error& e)
+ catch (const std::exception& e)
{
log<level::ERR>(e.what());
- rc = IPMI_CC_UNSPECIFIED_ERROR;
+ return ipmi::responseUnspecifiedError();
}
- return rc;
+ return ipmi::responseSuccess();
}
-ipmi_ret_t ipmi_storage_reserve_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief implements the reserve SEL command
+ * @returns IPMI completion code plus response data
+ * - SEL reservation ID.
+ */
+ipmi::RspType<uint16_t> ipmiStorageReserveSel()
{
- if (*data_len != 0)
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- ipmi_ret_t rc = IPMI_CC_OK;
- unsigned short selResID = reserveSel();
-
- *data_len = sizeof(selResID);
-
- // Pack the actual response
- std::memcpy(response, &selResID, *data_len);
-
- return rc;
+ return ipmi::responseSuccess(reserveSel());
}
-ipmi_ret_t ipmi_storage_add_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief implements the Add SEL entry command
+ * @request
+ *
+ * - recordID ID used for SEL Record access
+ * - recordType Record Type
+ * - timeStamp Time when event was logged. LS byte first
+ * - generatorID software ID if event was generated from
+ * system software
+ * - evmRev event message format version
+ * - sensorType sensor type code for service that generated
+ * the event
+ * - sensorNumber number of sensors that generated the event
+ * - eventDir event dir
+ * - eventData event data field contents
+ *
+ * @returns ipmi completion code plus response data
+ * - RecordID of the Added SEL entry
+ */
+ipmi::RspType<uint16_t // recordID of the Added SEL entry
+ >
+ ipmiStorageAddSEL(uint16_t recordID, uint8_t recordType, uint32_t timeStamp,
+ uint16_t generatorID, uint8_t evmRev, uint8_t sensorType,
+ uint8_t sensorNumber, uint8_t eventDir,
+ std::array<uint8_t, eventDataSize> eventData)
{
- if (*data_len != sizeof(ipmi_add_sel_request_t))
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- ipmi_ret_t rc = IPMI_CC_OK;
- ipmi_add_sel_request_t* p = (ipmi_add_sel_request_t*)request;
- uint16_t recordid;
-
// Per the IPMI spec, need to cancel the reservation when a SEL entry is
// added
cancelSELReservation();
-
- recordid = ((uint16_t)p->eventdata[1] << 8) | p->eventdata[2];
-
- *data_len = sizeof(recordid);
-
- // Pack the actual response
- std::memcpy(response, &p->eventdata[1], 2);
-
// Hostboot sends SEL with OEM record type 0xDE to indicate that there is
// a maintenance procedure associated with eSEL record.
static constexpr auto procedureType = 0xDE;
- if (p->recordtype == procedureType)
+ if (recordType == procedureType)
{
// In the OEM record type 0xDE, byte 11 in the SEL record indicate the
// procedure number.
- createProcedureLogEntry(p->sensortype);
+ createProcedureLogEntry(sensorType);
}
- return rc;
+ return ipmi::responseSuccess(recordID);
}
-// Read FRU info area
-ipmi_ret_t ipmi_storage_get_fru_inv_area_info(
- ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
+/** @brief implements the get FRU Inventory Area Info command
+ *
+ * @returns IPMI completion code plus response data
+ * - FRU Inventory area size in bytes,
+ * - access bit
+ **/
+ipmi::RspType<uint16_t, // FRU Inventory area size in bytes,
+ uint8_t // access size (bytes / words)
+ >
+ ipmiStorageGetFruInvAreaInfo(uint8_t fruID)
{
- ipmi_ret_t rc = IPMI_CC_OK;
- const FruInvenAreaInfoRequest* reqptr =
- reinterpret_cast<const FruInvenAreaInfoRequest*>(request);
- auto iter = frus.find(reqptr->fruID);
+ auto iter = frus.find(fruID);
if (iter == frus.end())
{
- *data_len = 0;
- return IPMI_CC_SENSOR_INVALID;
+ return ipmi::responseSensorInvalid();
}
try
{
- const auto& fruArea = getFruAreaData(reqptr->fruID);
- auto size = static_cast<uint16_t>(fruArea.size());
- FruInvenAreaInfoResponse resp;
- resp.sizems = size >> 8;
- resp.sizels = size;
- resp.access = static_cast<uint8_t>(AccessMode::bytes);
-
- *data_len = sizeof(resp);
-
- // Pack the actual response
- std::memcpy(response, &resp, *data_len);
+ return ipmi::responseSuccess(
+ static_cast<uint16_t>(getFruAreaData(fruID).size()),
+ static_cast<uint8_t>(AccessMode::bytes));
}
catch (const InternalFailure& e)
{
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- *data_len = 0;
log<level::ERR>(e.what());
+ return ipmi::responseUnspecifiedError();
}
- return rc;
}
-// Read FRU data
-ipmi_ret_t ipmi_storage_read_fru_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/**@brief implements the Read FRU Data command
+ * @param fruDeviceId - FRU device ID. FFh = reserved
+ * @param offset - FRU inventory offset to read
+ * @param readCount - count to read
+ *
+ * @return IPMI completion code plus response data
+ * - returnCount - response data count.
+ * - data - response data
+ */
+ipmi::RspType<uint8_t, // count returned
+ std::vector<uint8_t>> // FRU data
+ ipmiStorageReadFruData(uint8_t fruDeviceId, uint16_t offset,
+ uint8_t readCount)
{
- ipmi_ret_t rc = IPMI_CC_OK;
- const ReadFruDataRequest* reqptr =
- reinterpret_cast<const ReadFruDataRequest*>(request);
- auto resptr = reinterpret_cast<ReadFruDataResponse*>(response);
+ if (fruDeviceId == 0xFF)
+ {
+ return ipmi::responseInvalidFieldRequest();
+ }
- auto iter = frus.find(reqptr->fruID);
+ auto iter = frus.find(fruDeviceId);
if (iter == frus.end())
{
- *data_len = 0;
- return IPMI_CC_SENSOR_INVALID;
+ return ipmi::responseSensorInvalid();
}
- auto offset =
- static_cast<uint16_t>(reqptr->offsetMS << 8 | reqptr->offsetLS);
try
{
- const auto& fruArea = getFruAreaData(reqptr->fruID);
+ const auto& fruArea = getFruAreaData(fruDeviceId);
auto size = fruArea.size();
if (offset >= size)
{
- return IPMI_CC_PARM_OUT_OF_RANGE;
+ return ipmi::responseParmOutOfRange();
}
// Write the count of response data.
- if ((offset + reqptr->count) <= size)
+ uint8_t returnCount;
+ if ((offset + readCount) <= size)
{
- resptr->count = reqptr->count;
+ returnCount = readCount;
}
else
{
- resptr->count = size - offset;
+ returnCount = size - offset;
}
- std::copy((fruArea.begin() + offset),
- (fruArea.begin() + offset + resptr->count), resptr->data);
+ std::vector<uint8_t> fruData((fruArea.begin() + offset),
+ (fruArea.begin() + offset + returnCount));
- *data_len = resptr->count + 1; // additional one byte for count
+ return ipmi::responseSuccess(returnCount, fruData);
}
catch (const InternalFailure& e)
{
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- *data_len = 0;
log<level::ERR>(e.what());
+ return ipmi::responseUnspecifiedError();
}
- return rc;
}
-ipmi_ret_t ipmi_get_repository_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+ipmi::RspType<uint8_t, // SDR version
+ uint16_t, // record count LS first
+ uint16_t, // free space in bytes, LS first
+ uint32_t, // addition timestamp LS first
+ uint32_t, // deletion timestamp LS first
+ uint8_t> // operation Support
+ ipmiGetRepositoryInfo()
{
- constexpr auto sdrVersion = 0x51;
- auto responseData = reinterpret_cast<GetRepositoryInfoResponse*>(response);
-
- std::memset(responseData, 0, sizeof(GetRepositoryInfoResponse));
- responseData->sdrVersion = sdrVersion;
+ constexpr uint8_t sdrVersion = 0x51;
+ constexpr uint16_t freeSpace = 0xFFFF;
+ constexpr uint32_t additionTimestamp = 0x0;
+ constexpr uint32_t deletionTimestamp = 0x0;
+ constexpr uint8_t operationSupport = 0;
- uint16_t records = frus.size() + sensors.size();
- responseData->recordCountMs = records >> 8;
- responseData->recordCountLs = records;
+ uint16_t records = frus.size() + ipmi::sensor::sensors.size();
- responseData->freeSpace[0] = 0xFF;
- responseData->freeSpace[1] = 0xFF;
-
- *data_len = sizeof(GetRepositoryInfoResponse);
-
- return IPMI_CC_OK;
+ return ipmi::responseSuccess(sdrVersion, records, freeSpace,
+ additionTimestamp, deletionTimestamp,
+ operationSupport);
}
void register_netfn_storage_functions()
{
- // <Wildcard Command>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WILDCARD, NULL,
- ipmi_storage_wildcard, PRIVILEGE_USER);
-
// <Get SEL Info>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_INFO, NULL,
- getSELInfo, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
+ ipmiStorageGetSelInfo);
// <Get SEL Time>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_TIME, NULL,
- ipmi_storage_get_sel_time, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
+ ipmiStorageGetSelTime);
// <Set SEL Time>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_SET_SEL_TIME, NULL,
- ipmi_storage_set_sel_time, PRIVILEGE_OPERATOR);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdSetSelTime,
+ ipmi::Privilege::Operator, ipmiStorageSetSelTime);
// <Reserve SEL>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL, NULL,
- ipmi_storage_reserve_sel, PRIVILEGE_USER);
-
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdReserveSel, ipmi::Privilege::User,
+ ipmiStorageReserveSel);
// <Get SEL Entry>
ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL,
getSELEntry, PRIVILEGE_USER);
// <Delete SEL Entry>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_DELETE_SEL, NULL,
- deleteSELEntry, PRIVILEGE_OPERATOR);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdDeleteSelEntry,
+ ipmi::Privilege::Operator, deleteSELEntry);
// <Add SEL Entry>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL,
- ipmi_storage_add_sel, PRIVILEGE_OPERATOR);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdAddSelEntry,
+ ipmi::Privilege::Operator, ipmiStorageAddSEL);
+
// <Clear SEL>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_CLEAR_SEL, NULL, clearSEL,
- PRIVILEGE_OPERATOR);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
+ clearSEL);
+
// <Get FRU Inventory Area Info>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_FRU_INV_AREA_INFO, NULL,
- ipmi_storage_get_fru_inv_area_info,
- PRIVILEGE_OPERATOR);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdGetFruInventoryAreaInfo,
+ ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
- // <Add READ FRU Data
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_READ_FRU_DATA, NULL,
- ipmi_storage_read_fru_data, PRIVILEGE_OPERATOR);
+ // <READ FRU Data>
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdReadFruData,
+ ipmi::Privilege::Operator, ipmiStorageReadFruData);
// <Get Repository Info>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_REPOSITORY_INFO,
- nullptr, ipmi_get_repository_info, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdGetSdrRepositoryInfo,
+ ipmi::Privilege::User, ipmiGetRepositoryInfo);
// <Reserve SDR Repository>
- ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SDR, nullptr,
- ipmi_sen_reserve_sdr, PRIVILEGE_USER);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
+ ipmi::storage::cmdReserveSdrRepository,
+ ipmi::Privilege::User, ipmiSensorReserveSdr);
// <Get SDR>
ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr,
diff --git a/storagehandler.hpp b/storagehandler.hpp
index bcc6c7d..6c91905 100644
--- a/storagehandler.hpp
+++ b/storagehandler.hpp
@@ -21,69 +21,3 @@ enum ipmi_netfn_storage_cmds
IPMI_CMD_SET_SEL_TIME = 0x49,
};
-
-struct ipmi_add_sel_request_t
-{
-
- uint8_t recordid[2];
- uint8_t recordtype;
- uint8_t timestamp[4];
- uint8_t generatorid[2];
- uint8_t evmrev;
- uint8_t sensortype;
- uint8_t sensornumber;
- uint8_t eventdir;
- uint8_t eventdata[3];
-};
-
-/**
- * @struct Read FRU Data command request data
- */
-struct ReadFruDataRequest
-{
- uint8_t fruID; ///< FRU Device ID. FFh = reserved
- uint8_t offsetLS; ///< FRU Inventory Offset to read, LS Byte
- uint8_t offsetMS; ///< FRU Inventory Offset ro read, MS Byte
- uint8_t count; ///< Count to read
-} __attribute__((packed));
-
-/**
- * @struct Read FRU Data command response data
- */
-struct ReadFruDataResponse
-{
- uint8_t count; ///< Response data Count.
- uint8_t data[]; ///< Response data.
-} __attribute__((packed));
-
-/**
- * @struct Get FRU inventory area info command request data
- */
-struct FruInvenAreaInfoRequest
-{
- uint8_t fruID; ///< FRU Device ID. FFH = reserved.
-} __attribute__((packed));
-
-/**
- * @struct Get FRU inventory area info command response
- */
-struct FruInvenAreaInfoResponse
-{
- uint8_t sizels; ///< Fru Inventory area size in bytes, LS Byte
- uint8_t sizems; ///< Fru Inventory are size in bytes, MS Byte
- uint8_t access; ///< 0b Devices is accessed by bytes, 1b - by words
-} __attribute__((packed));
-
-/**
- * @struct Get Repository info command response
- */
-struct GetRepositoryInfoResponse
-{
- uint8_t sdrVersion; //< SDR version
- uint8_t recordCountLs; //< Record count LS byte
- uint8_t recordCountMs; //< Record count MS bte
- uint8_t freeSpace[2]; //< Free space in bytes, LS first
- uint8_t additionTimestamp[4]; //< Most recent addition timestamp LS first
- uint8_t deletionTimestamp[4]; //< Most recent deletion timestamp LS first
- uint8_t operationSupport; //< Operation support
-} __attribute__((packed));
diff --git a/systemintfcmds.cpp b/systemintfcmds.cpp
index d298abd..689b465 100644
--- a/systemintfcmds.cpp
+++ b/systemintfcmds.cpp
@@ -17,17 +17,6 @@ using namespace sdbusplus::xyz::openbmc_project::Control::server;
using cmdManagerPtr = std::unique_ptr<phosphor::host::command::Manager>;
extern cmdManagerPtr& ipmid_get_host_cmd_manager();
-// global enables
-// bit0 - Message Receive Queue enable
-// bit1 - Enable Event Message Buffer Full Interrupt
-// bit2 - Enable Event Message Buffer
-// bit3 - Enable System Event Logging
-// bit4 - reserved
-// bit5-7 - OEM 0~2 enables
-static constexpr uint8_t selEnable = 0x08;
-static constexpr uint8_t recvMsgQueueEnable = 0x01;
-static constexpr uint8_t globalEnablesDefault = selEnable | recvMsgQueueEnable;
-
//-------------------------------------------------------------------
// Called by Host post response from Get_Message_Flags
//-------------------------------------------------------------------
@@ -71,71 +60,68 @@ ipmi_ret_t ipmi_app_read_event(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
// Called by Host on seeing a SMS_ATN bit set. Return a hardcoded
// value of 0x2 indicating we need Host read some data.
//-------------------------------------------------------------------
-ipmi_ret_t ipmi_app_get_msg_flags(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+ipmi::RspType<uint8_t> ipmiAppGetMessageFlags()
{
- // Generic return from IPMI commands.
- ipmi_ret_t rc = IPMI_CC_OK;
-
// From IPMI spec V2.0 for Get Message Flags Command :
// bit:[1] from LSB : 1b = Event Message Buffer Full.
// Return as 0 if Event Message Buffer is not supported,
// or when the Event Message buffer is disabled.
- // For now, it is not supported.
-
- uint8_t set_event_msg_buffer_full = 0x0;
- *data_len = sizeof(set_event_msg_buffer_full);
-
- // Pack the actual response
- std::memcpy(response, &set_event_msg_buffer_full, *data_len);
-
- return rc;
+ // This path is used to communicate messages to the host
+ // from within the phosphor::host::command::Manager
+ constexpr uint8_t setEventMsgBufferFull = 0x2;
+ return ipmi::responseSuccess(setEventMsgBufferFull);
}
-ipmi_ret_t ipmi_app_get_bmc_global_enables(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+ipmi::RspType<bool, // Receive Message Queue Interrupt Enabled
+ bool, // Event Message Buffer Full Interrupt Enabled
+ bool, // Event Message Buffer Enabled
+ bool, // System Event Logging Enabled
+ uint1_t, // Reserved
+ bool, // OEM 0 enabled
+ bool, // OEM 1 enabled
+ bool // OEM 2 enabled
+ >
+ ipmiAppGetBMCGlobalEnable()
{
- ipmi_ret_t rc = IPMI_CC_OK;
- if (0 != *data_len)
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
- *data_len = sizeof(globalEnablesDefault);
- *reinterpret_cast<uint8_t*>(response) = globalEnablesDefault;
- return rc;
+ return ipmi::responseSuccess(true, false, false, true, 0, false, false,
+ false);
}
-ipmi_ret_t ipmi_app_set_bmc_global_enables(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+ipmi::RspType<> ipmiAppSetBMCGlobalEnable(
+ ipmi::Context::ptr ctx, bool receiveMessageQueueInterruptEnabled,
+ bool eventMessageBufferFullInterruptEnabled, bool eventMessageBufferEnabled,
+ bool systemEventLogEnable, uint1_t reserved, bool OEM0Enabled,
+ bool OEM1Enabled, bool OEM2Enabled)
{
- ipmi_ret_t rc = IPMI_CC_OK;
+ ipmi::ChannelInfo chInfo;
- uint8_t reqMask = *reinterpret_cast<uint8_t*>(request);
- if (sizeof(reqMask) != *data_len)
+ if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
{
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to get Channel Info",
+ phosphor::logging::entry("CHANNEL=%d", ctx->channel));
+ return ipmi::responseUnspecifiedError();
+ }
+
+ if (chInfo.mediumType !=
+ static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error - supported only in system interface");
+ return ipmi::responseCommandNotAvailable();
}
- *data_len = 0;
// Recv Message Queue and SEL are enabled by default.
// Event Message buffer are disabled by default (not supported).
// Any request that try to change the mask will be rejected
- if (reqMask != (selEnable | recvMsgQueueEnable))
+ if (!receiveMessageQueueInterruptEnabled || !systemEventLogEnable ||
+ eventMessageBufferFullInterruptEnabled || eventMessageBufferEnabled ||
+ OEM0Enabled || OEM1Enabled || OEM2Enabled || reserved)
{
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return ipmi::responseInvalidFieldRequest();
}
- return rc;
+
+ return ipmi::responseSuccess();
}
namespace
@@ -155,16 +141,19 @@ void register_netfn_app_functions()
ipmi_app_read_event, SYSTEM_INTERFACE);
// <Set BMC Global Enables>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_BMC_GLOBAL_ENABLES, NULL,
- ipmi_app_set_bmc_global_enables, SYSTEM_INTERFACE);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdSetBmcGlobalEnables,
+ ipmi::Privilege::Admin, ipmiAppSetBMCGlobalEnable);
// <Get BMC Global Enables>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_BMC_GLOBAL_ENABLES, NULL,
- ipmi_app_get_bmc_global_enables, SYSTEM_INTERFACE);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdGetBmcGlobalEnables,
+ ipmi::Privilege::User, ipmiAppGetBMCGlobalEnable);
// <Get Message Flags>
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_MSG_FLAGS, NULL,
- ipmi_app_get_msg_flags, SYSTEM_INTERFACE);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+ ipmi::app::cmdGetMessageFlags, ipmi::Privilege::Admin,
+ ipmiAppGetMessageFlags);
// Create new xyz.openbmc_project.host object on the bus
auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';
diff --git a/test/Makefile.am b/test/Makefile.am
index e510230..0faeda7 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -26,6 +26,11 @@ AM_LDFLAGS = $(GTEST_MAIN_LIBS) $(OESDK_TESTCASE_FLAGS)
check_PROGRAMS =
TESTS = $(check_PROGRAMS)
+entitymap_json_unittest_SOURCES = entitymap_json_unittest.cpp
+entitymap_json_unittest_LDADD = $(top_builddir)/entity_map_json.o -lgmock
+
+check_PROGRAMS += entitymap_json_unittest
+
# Build/add sample_unittest to test suite
sample_unittest_CPPFLAGS = -Igtest $(GTEST_CPPFLAGS) $(AM_CPPFLAGS)
sample_unittest_CXXFLAGS = $(PTHREAD_CFLAGS) $(CODE_COVERAGE_CXXFLAGS) \
@@ -67,3 +72,21 @@ message_unittest_SOURCES = \
%reldir%/message/unpack.cpp \
%reldir%/message/pack.cpp
check_PROGRAMS += %reldir%/message_unittest
+
+# Build/add closesession_unittest to test suite
+session_unittest_CPPFLAGS = \
+ -Igtest \
+ $(GTEST_CPPFLAGS) \
+ $(AM_CPPFLAGS)
+session_unittest_CXXFLAGS = \
+ $(PTHREAD_CFLAGS) \
+ $(CODE_COVERAGE_CXXFLAGS) \
+ $(CODE_COVERAGE_CFLAGS)
+session_unittest_LDFLAGS = \
+ -lgtest_main \
+ -lgtest \
+ -pthread \
+ $(OESDK_TESTCASE_FLAGS) \
+ $(CODE_COVERAGE_LDFLAGS)
+session_unittest_SOURCES = %reldir%/session/closesession_unittest.cpp
+check_PROGRAMS += %reldir%/session_unittest
diff --git a/test/entitymap_json_unittest.cpp b/test/entitymap_json_unittest.cpp
new file mode 100644
index 0000000..a380a9e
--- /dev/null
+++ b/test/entitymap_json_unittest.cpp
@@ -0,0 +1,223 @@
+#include "entity_map_json.hpp"
+
+#include <ipmid/types.hpp>
+#include <nlohmann/json.hpp>
+#include <utility>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace ipmi
+{
+namespace sensor
+{
+
+namespace
+{
+using ::testing::IsEmpty;
+
+TEST(ValidateJson, FailWithNonArrayReturnsEmpty)
+{
+ /* The entity map input json is expected to be an array of objects. */
+ auto j = R"(
+ {
+ "id" : 1,
+ "containerEntityId" : 2,
+ "containerEntityInstance" : 3,
+ "isList" : false,
+ "isLinked" : false,
+ "entities" : [
+ {"id" : 1, "instance" : 2},
+ {"id" : 1, "instance" : 3},
+ {"id" : 1, "instance" : 4},
+ {"id" : 1, "instance" : 5}
+ ]
+ }
+ )"_json;
+
+ EXPECT_THAT(buildJsonEntityMap(j), IsEmpty());
+}
+
+TEST(ValidateJson, FailWithMissingFieldReturnsEmpty)
+{
+ /* There are many required fields, let's just validate that if one is
+ * missing, it returns the empty map.
+ */
+ auto j = R"(
+ [
+ {
+ "id" : 1,
+ "containerEntityId" : 2,
+ "containerEntityInstance" : 3,
+ "isList" : false,
+ "entities" : [
+ {"id" : 1, "instance" : 2},
+ {"id" : 1, "instance" : 3},
+ {"id" : 1, "instance" : 4},
+ {"id" : 1, "instance" : 5}
+ ]
+ }
+ ]
+ )"_json;
+
+ EXPECT_THAT(buildJsonEntityMap(j), IsEmpty());
+}
+
+TEST(ValidateJson, AllValidEntryReturnsExpectedMap)
+{
+ /* Boring test where we provide completely valid information and expect the
+ * resulting map contains that information.
+ */
+ auto j = R"(
+ [
+ {
+ "id" : 1,
+ "containerEntityId" : 2,
+ "containerEntityInstance" : 3,
+ "isList" : false,
+ "isLinked" : false,
+ "entities" : [
+ {"id" : 1, "instance" : 2},
+ {"id" : 1, "instance" : 3},
+ {"id" : 1, "instance" : 4},
+ {"id" : 1, "instance" : 5}
+ ]
+ }
+ ]
+ )"_json;
+
+ auto map = buildJsonEntityMap(j);
+ EXPECT_FALSE(map.find(1) == map.end());
+ auto entry = map.find(1);
+ EXPECT_EQ(entry->first, 1);
+
+ /* TODO: someone could write an equality operator for this object. */
+ EXPECT_EQ(entry->second.containerEntityId, 2);
+ EXPECT_EQ(entry->second.containerEntityInstance, 3);
+ EXPECT_FALSE(entry->second.isList);
+ EXPECT_FALSE(entry->second.isLinked);
+ ContainedEntitiesArray expected = {
+ std::make_pair(1, 2), std::make_pair(1, 3), std::make_pair(1, 4),
+ std::make_pair(1, 5)};
+ EXPECT_EQ(entry->second.containedEntities, expected);
+}
+
+TEST(ValidateJson, EntryHasInsufficientContainerEntryCountReturnsEmpty)
+{
+ /* The container must have four pairs. (I don't know why, and maybe this
+ * restriction will change).
+ */
+ auto j = R"(
+ [
+ {
+ "id" : 1,
+ "containerEntityId" : 2,
+ "containerEntityInstance" : 3,
+ "isList" : false,
+ "isLinked" : false,
+ "entities" : [
+ {"id" : 1, "instance" : 2},
+ {"id" : 1, "instance" : 3},
+ {"id" : 1, "instance" : 4}
+ ]
+ }
+ ]
+ )"_json;
+
+ EXPECT_THAT(buildJsonEntityMap(j), IsEmpty());
+}
+
+TEST(ValidateJson, ThereAreTwoEntriesOneInvalidReturnsEmpty)
+{
+ /* If any entry in the file is corrupt, the file is disregarded. */
+ auto j = R"(
+ [
+ {
+ "id" : 1,
+ "containerEntityId" : 2,
+ "containerEntityInstance" : 3,
+ "isList" : false,
+ "isLinked" : false,
+ "entities" : [
+ {"id" : 1, "instance" : 2},
+ {"id" : 1, "instance" : 3},
+ {"id" : 1, "instance" : 4},
+ {"id" : 1, "instance" : 5}
+ ]
+ },
+ {
+ "id" : 2,
+ "containerEntityId" : 2,
+ "containerEntityInstance" : 3,
+ "isList" : false,
+ "isLinked" : false,
+ "entities" : [
+ {"id" : 1, "instance" : 2},
+ {"id" : 1, "instance" : 3},
+ {"id" : 1, "instance" : 4}
+ ]
+ }
+ ]
+ )"_json;
+
+ EXPECT_THAT(buildJsonEntityMap(j), IsEmpty());
+}
+
+TEST(ValidateJson, ThereAreTwoEntriesBothValidReturnsBoth)
+{
+ /* The map supports more than one entry, just validate this. */
+ auto j = R"(
+ [
+ {
+ "id" : 1,
+ "containerEntityId" : 2,
+ "containerEntityInstance" : 3,
+ "isList" : false,
+ "isLinked" : false,
+ "entities" : [
+ {"id" : 1, "instance" : 2},
+ {"id" : 1, "instance" : 3},
+ {"id" : 1, "instance" : 4},
+ {"id" : 1, "instance" : 5}
+ ]
+ },
+ {
+ "id" : 2,
+ "containerEntityId" : 2,
+ "containerEntityInstance" : 3,
+ "isList" : false,
+ "isLinked" : false,
+ "entities" : [
+ {"id" : 1, "instance" : 6},
+ {"id" : 1, "instance" : 7},
+ {"id" : 1, "instance" : 8},
+ {"id" : 1, "instance" : 9}
+ ]
+ }
+ ]
+ )"_json;
+
+ auto map = buildJsonEntityMap(j);
+ EXPECT_FALSE(map.find(1) == map.end());
+ EXPECT_FALSE(map.find(2) == map.end());
+
+ auto entry = map.find(1);
+ EXPECT_EQ(entry->first, 1);
+ EXPECT_EQ(entry->second.containerEntityId, 2);
+ EXPECT_EQ(entry->second.containerEntityInstance, 3);
+ EXPECT_FALSE(entry->second.isList);
+ EXPECT_FALSE(entry->second.isLinked);
+ ContainedEntitiesArray expected = {
+ std::make_pair(1, 2), std::make_pair(1, 3), std::make_pair(1, 4),
+ std::make_pair(1, 5)};
+ EXPECT_EQ(entry->second.containedEntities, expected);
+
+ entry = map.find(2);
+ expected = {std::make_pair(1, 6), std::make_pair(1, 7),
+ std::make_pair(1, 8), std::make_pair(1, 9)};
+ EXPECT_EQ(entry->second.containedEntities, expected);
+}
+
+} // namespace
+} // namespace sensor
+} // namespace ipmi
diff --git a/test/message/pack.cpp b/test/message/pack.cpp
index d0a738a..9e88f2b 100644
--- a/test/message/pack.cpp
+++ b/test/message/pack.cpp
@@ -235,6 +235,27 @@ TEST(PackBasics, VectorUint8)
ASSERT_EQ(p.raw, k);
}
+TEST(PackBasics, VectorUnaligned)
+{
+ ipmi::message::Payload p;
+ EXPECT_EQ(p.pack(true, std::vector<uint8_t>{1}), 1);
+ EXPECT_EQ(p.raw, std::vector<uint8_t>{0b1});
+}
+
+TEST(PackBasics, StringView)
+{
+ ipmi::message::Payload p;
+ EXPECT_EQ(p.pack(std::string_view{"\x24\x30\x11"}), 0);
+ EXPECT_EQ(p.raw, std::vector<uint8_t>({0x24, 0x30, 0x11}));
+}
+
+TEST(PackBasics, StringViewUnaligned)
+{
+ ipmi::message::Payload p;
+ EXPECT_EQ(p.pack(true, std::string_view{"abc"}), 1);
+ EXPECT_EQ(p.raw, std::vector<uint8_t>({0b1}));
+}
+
TEST(PackBasics, OptionalEmpty)
{
// an optional will only pack if the value is present
@@ -261,6 +282,56 @@ TEST(PackBasics, OptionalContainsValue)
ASSERT_EQ(p.raw, k);
}
+TEST(PackBasics, Payload)
+{
+ ipmi::message::Payload p;
+ EXPECT_EQ(p.pack(true), 0);
+ EXPECT_EQ(p.pack(ipmi::message::Payload({0x24, 0x30})), 0);
+ EXPECT_EQ(p.raw, std::vector<uint8_t>({0b1, 0x24, 0x30}));
+}
+
+TEST(PackBasics, PayloadUnaligned)
+{
+ ipmi::message::Payload p;
+ EXPECT_EQ(p.pack(true, ipmi::message::Payload({0x24})), 1);
+ EXPECT_EQ(p.raw, std::vector<uint8_t>({0b1}));
+}
+
+TEST(PackBasics, PayloadOtherUnaligned)
+{
+ ipmi::message::Payload p, q;
+ q.appendBits(1, 1);
+ EXPECT_EQ(p.pack(true), 0);
+ EXPECT_EQ(p.pack(q), 1);
+ EXPECT_EQ(p.raw, std::vector<uint8_t>({0b1}));
+}
+
+TEST(PackBasics, PrependPayload)
+{
+ ipmi::message::Payload p;
+ EXPECT_EQ(p.pack(true), 0);
+ EXPECT_EQ(p.prepend(ipmi::message::Payload({0x24, 0x30})), 0);
+ EXPECT_EQ(p.raw, std::vector<uint8_t>({0x24, 0x30, 0b1}));
+}
+
+TEST(PackBasics, PrependPayloadUnaligned)
+{
+ ipmi::message::Payload p;
+ p.appendBits(1, 1);
+ EXPECT_EQ(p.prepend(ipmi::message::Payload({0x24})), 1);
+ p.drain();
+ EXPECT_EQ(p.raw, std::vector<uint8_t>({0b1}));
+}
+
+TEST(PackBasics, PrependPayloadOtherUnaligned)
+{
+ ipmi::message::Payload p, q;
+ q.appendBits(1, 1);
+ EXPECT_EQ(p.pack(true), 0);
+ EXPECT_EQ(p.prepend(q), 1);
+ EXPECT_EQ(p.raw, std::vector<uint8_t>({0b1}));
+}
+
TEST(PackAdvanced, Uints)
{
// all elements will be processed in order, with each multi-byte
diff --git a/test/message/payload.cpp b/test/message/payload.cpp
index 9d20ff0..56d8d41 100644
--- a/test/message/payload.cpp
+++ b/test/message/payload.cpp
@@ -13,8 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define SD_JOURNAL_SUPPRESS_LOCATION
+
+#include <systemd/sd-journal.h>
+
#include <ipmid/api.hpp>
#include <ipmid/message.hpp>
+#include <stdexcept>
#include <gtest/gtest.h>
@@ -306,3 +311,132 @@ TEST(PayloadRequest, PartialPayload)
uint32_t k2 = 0x02008604;
ASSERT_EQ(v2, k2);
}
+
+std::vector<std::string> logs;
+
+extern "C" {
+int sd_journal_send(const char* format, ...)
+{
+ logs.push_back(format);
+ return 0;
+}
+
+int sd_journal_send_with_location(const char* file, const char* line,
+ const char* func, const char* format, ...)
+{
+ logs.push_back(format);
+ return 0;
+}
+}
+
+class PayloadLogging : public testing::Test
+{
+ public:
+ void SetUp()
+ {
+ logs.clear();
+ }
+};
+
+TEST_F(PayloadLogging, TrailingOk)
+{
+ {
+ ipmi::message::Payload p({1, 2});
+ }
+ EXPECT_EQ(logs.size(), 0);
+}
+
+TEST_F(PayloadLogging, EnforcingUnchecked)
+{
+ {
+ ipmi::message::Payload p({1, 2});
+ p.trailingOk = false;
+ }
+ EXPECT_EQ(logs.size(), 1);
+}
+
+TEST_F(PayloadLogging, EnforcingUncheckedUnpacked)
+{
+ {
+ ipmi::message::Payload p({1, 2});
+ p.trailingOk = false;
+ uint8_t out;
+ p.unpack(out, out);
+ }
+ EXPECT_EQ(logs.size(), 1);
+}
+
+TEST_F(PayloadLogging, EnforcingUncheckedError)
+{
+ {
+ ipmi::message::Payload p({1, 2});
+ p.trailingOk = false;
+ uint32_t out;
+ p.unpack(out);
+ }
+ EXPECT_EQ(logs.size(), 0);
+}
+
+TEST_F(PayloadLogging, EnforcingChecked)
+{
+ {
+ ipmi::message::Payload p({1, 2});
+ p.trailingOk = false;
+ EXPECT_FALSE(p.fullyUnpacked());
+ }
+ EXPECT_EQ(logs.size(), 0);
+}
+
+TEST_F(PayloadLogging, EnforcingCheckedUnpacked)
+{
+ {
+ ipmi::message::Payload p({1, 2});
+ p.trailingOk = false;
+ uint8_t out;
+ p.unpack(out, out);
+ EXPECT_TRUE(p.fullyUnpacked());
+ }
+ EXPECT_EQ(logs.size(), 0);
+}
+
+TEST_F(PayloadLogging, EnforcingUnpackPayload)
+{
+ {
+ ipmi::message::Payload p;
+ {
+ ipmi::message::Payload q({1, 2});
+ q.trailingOk = false;
+ q.unpack(p);
+ }
+ EXPECT_EQ(logs.size(), 0);
+ }
+ EXPECT_EQ(logs.size(), 1);
+}
+
+TEST_F(PayloadLogging, EnforcingMove)
+{
+ {
+ ipmi::message::Payload p;
+ {
+ ipmi::message::Payload q({1, 2});
+ q.trailingOk = false;
+ p = std::move(q);
+ }
+ EXPECT_EQ(logs.size(), 0);
+ }
+ EXPECT_EQ(logs.size(), 1);
+}
+
+TEST_F(PayloadLogging, EnforcingException)
+{
+ try
+ {
+ ipmi::message::Payload p({1, 2});
+ p.trailingOk = false;
+ throw std::runtime_error("test");
+ }
+ catch (...)
+ {
+ }
+ EXPECT_EQ(logs.size(), 0);
+}
diff --git a/test/message/unpack.cpp b/test/message/unpack.cpp
index 611a5fe..7d69218 100644
--- a/test/message/unpack.cpp
+++ b/test/message/unpack.cpp
@@ -660,8 +660,8 @@ TEST(Vectors, VectorUint32NonIntegralBytes)
0x99, 0x77, 0x55, 0x33, 0x78, 0x56, 0x34};
ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
std::vector<uint32_t> v;
- // check that the number of bytes matches
- ASSERT_NE(p.unpack(v), 0);
+ // check that the vector unpacks successfully
+ ASSERT_EQ(p.unpack(v), 0);
// check that the payload was not fully unpacked
ASSERT_FALSE(p.fullyUnpacked());
// arrays of uint32_t will be unpacked one at a time, so the
@@ -686,6 +686,51 @@ TEST(Vectors, VectorUint8)
ASSERT_EQ(v, k);
}
+TEST(Vectors, VectorEmptyOk)
+{
+ // an empty input vector to show that unpacking elements is okay
+ std::vector<uint8_t> i{};
+ ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
+ std::vector<uint32_t> v;
+ // check that the number of bytes matches
+ ASSERT_EQ(p.unpack(v), 0);
+ // check that the payload was fully unpacked
+ ASSERT_TRUE(p.fullyUnpacked());
+ std::vector<uint32_t> k{};
+ // check that the unpacked vector is empty as expected
+ ASSERT_EQ(v, k);
+}
+
+TEST(Vectors, VectorOfTuplesOk)
+{
+ // a vector of bytes will be unpacked verbatim, low-order element first
+ std::vector<uint8_t> i = {0x02, 0x00, 0x86, 0x04};
+ ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
+ std::vector<std::tuple<uint8_t, uint8_t>> v;
+ // check that the number of bytes matches
+ ASSERT_EQ(p.unpack(v), 0);
+ // check that the payload was fully unpacked
+ ASSERT_TRUE(p.fullyUnpacked());
+ std::vector<std::tuple<uint8_t, uint8_t>> k = {{0x02, 0x00}, {0x86, 0x04}};
+ // check that the bytes were correctly unpacked (in byte order)
+ ASSERT_EQ(v, k);
+}
+
+TEST(Vectors, VectorOfTuplesInsufficientBytes)
+{
+ // a vector of bytes will be unpacked verbatim, low-order element first
+ std::vector<uint8_t> i = {0x02, 0x00, 0x86, 0x04, 0xb4};
+ ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
+ std::vector<std::tuple<uint8_t, uint8_t>> v;
+ // check that the number of bytes matches
+ ASSERT_EQ(p.unpack(v), 0);
+ // check that the payload was not fully unpacked
+ ASSERT_FALSE(p.fullyUnpacked());
+ std::vector<std::tuple<uint8_t, uint8_t>> k = {{0x02, 0x00}, {0x86, 0x04}};
+ // check that the bytes were correctly unpacked (in byte order)
+ ASSERT_EQ(v, k);
+}
+
// Cannot test TooManyBytes or InsufficientBytes for vector<uint8_t>
// because it will always unpack whatever bytes are remaining
// TEST(Vectors, VectorUint8TooManyBytes) {}
@@ -716,7 +761,7 @@ TEST(UnpackAdvanced, OptionalInsufficientBytes)
ASSERT_EQ(p.unpack(v), 0);
// check that the payload was fully unpacked
ASSERT_FALSE(p.fullyUnpacked());
- std::optional<std::tuple<uint8_t, uint32_t>> k = {{0, 0}};
+ std::optional<std::tuple<uint8_t, uint32_t>> k;
// check that the bytes were correctly unpacked (in byte order)
ASSERT_EQ(v, k);
}
diff --git a/test/session/closesession_unittest.cpp b/test/session/closesession_unittest.cpp
new file mode 100644
index 0000000..2b184ca
--- /dev/null
+++ b/test/session/closesession_unittest.cpp
@@ -0,0 +1,114 @@
+#include <ipmid/sessionhelper.hpp>
+
+#include <gtest/gtest.h>
+
+TEST(parseSessionInputPayloadTest, ValidObjectPath)
+{
+ uint32_t sessionId = 0;
+ uint8_t sessionHandle = 0;
+ std::string objectPath =
+ "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a";
+
+ EXPECT_TRUE(
+ parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle));
+ EXPECT_EQ(0x12a4567d, sessionId);
+ EXPECT_EQ(0x8a, sessionHandle);
+}
+
+TEST(parseSessionInputPayloadTest, InvalidObjectPath)
+{
+ uint32_t sessionId = 0;
+ uint8_t sessionHandle = 0;
+ // A valid object path will be like
+ // "/xyz/openbmc_project/ipmi/session/channel/sessionId_sessionHandle"
+ // Ex: "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"
+ // SessionId : 0X12a4567d
+ // SessionHandle: 0X8a
+ std::string objectPath = "/xyz/openbmc_project/ipmi/session/eth0/12a4567d";
+
+ EXPECT_FALSE(
+ parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle));
+}
+
+TEST(parseSessionInputPayloadTest, NoObjectPath)
+{
+ uint32_t sessionId = 0;
+ uint8_t sessionHandle = 0;
+ std::string objectPath;
+
+ EXPECT_FALSE(
+ parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle));
+}
+
+TEST(isSessionObjectMatchedTest, ValidSessionId)
+{
+ std::string objectPath =
+ "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a";
+ uint32_t sessionId = 0x12a4567d;
+ uint8_t sessionHandle = 0;
+
+ EXPECT_TRUE(isSessionObjectMatched(objectPath, sessionId, sessionHandle));
+}
+
+TEST(isSessionObjectMatchedTest, ValidSessionHandle)
+{
+ std::string objectPath =
+ "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a";
+ uint32_t sessionId = 0;
+ uint8_t sessionHandle = 0x8a;
+
+ EXPECT_TRUE(isSessionObjectMatched(objectPath, sessionId, sessionHandle));
+}
+
+TEST(isSessionObjectMatchedTest, InvalidSessionId)
+{
+ std::string objectPath =
+ "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a";
+ uint32_t sessionId = 0x1234b67d;
+ uint8_t sessionHandle = 0;
+
+ EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle));
+}
+
+TEST(isSessionObjectMatchedTest, InvalidSessionHandle)
+{
+ std::string objectPath =
+ "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a";
+ uint32_t sessionId = 0;
+ uint8_t sessionHandle = 0x9b;
+
+ EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle));
+}
+
+TEST(isSessionObjectMatchedTest, ZeroSessionId_ZeroSessionHandle)
+{
+ std::string objectPath =
+ "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a";
+ uint32_t sessionId = 0;
+ uint8_t sessionHandle = 0;
+
+ EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle));
+}
+
+TEST(isSessionObjectMatchedTest, InvalidObjectPath)
+{
+ // A valid object path will be like
+ // "/xyz/openbmc_project/ipmi/session/channel/sessionId_sessionHandle"
+ // Ex: "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"
+ // SessionId : 0X12a4567d
+ // SessionHandle: 0X8a
+ std::string objectPath = "/xyz/openbmc_project/ipmi/session/eth0/12a4567d";
+ uint32_t sessionId = 0x12a4567d;
+ uint8_t sessionHandle = 0;
+
+ EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle));
+}
+
+TEST(isSessionObjectMatchedTest, NoObjectPath)
+{
+ std::string objectPath;
+ uint32_t sessionId = 0x12a4567d;
+ uint8_t sessionHandle = 0x8a;
+
+ EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle));
+}
diff --git a/transporthandler.cpp b/transporthandler.cpp
index 59d933a..a3b3c35 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -1,1002 +1,1948 @@
-#include "transporthandler.hpp"
-
#include "app/channel.hpp"
-#include "user_channel/channel_layer.hpp"
#include <arpa/inet.h>
+#include <netinet/ether.h>
-#include <chrono>
-#include <filesystem>
+#include <array>
+#include <bitset>
+#include <cinttypes>
+#include <cstdint>
+#include <cstring>
#include <fstream>
+#include <functional>
#include <ipmid/api.hpp>
+#include <ipmid/message.hpp>
+#include <ipmid/message/types.hpp>
+#include <ipmid/types.hpp>
#include <ipmid/utils.hpp>
+#include <optional>
#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
#include <phosphor-logging/log.hpp>
-#include <sdbusplus/message/types.hpp>
-#include <sdbusplus/timer.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/exception.hpp>
#include <string>
+#include <string_view>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <user_channel/channel_layer.hpp>
+#include <utility>
+#include <vector>
#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/Network/IP/server.hpp>
+#include <xyz/openbmc_project/Network/Neighbor/server.hpp>
+
+using phosphor::logging::commit;
+using phosphor::logging::elog;
+using phosphor::logging::entry;
+using phosphor::logging::level;
+using phosphor::logging::log;
+using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using sdbusplus::xyz::openbmc_project::Network::server::IP;
+using sdbusplus::xyz::openbmc_project::Network::server::Neighbor;
-#define SYSTEMD_NETWORKD_DBUS 1
+namespace cipher
+{
-#ifdef SYSTEMD_NETWORKD_DBUS
-#include <mapper.h>
-#include <systemd/sd-bus.h>
-#endif
+std::vector<uint8_t> getCipherList()
+{
+ std::vector<uint8_t> cipherList;
-// timer for network changes
-std::unique_ptr<phosphor::Timer> networkTimer = nullptr;
+ std::ifstream jsonFile(cipher::configFile);
+ if (!jsonFile.is_open())
+ {
+ log<level::ERR>("Channel Cipher suites file not found");
+ elog<InternalFailure>();
+ }
-const int SIZE_MAC = 18; // xx:xx:xx:xx:xx:xx
-constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
+ auto data = Json::parse(jsonFile, nullptr, false);
+ if (data.is_discarded())
+ {
+ log<level::ERR>("Parsing channel cipher suites JSON failed");
+ elog<InternalFailure>();
+ }
+
+ // Byte 1 is reserved
+ cipherList.push_back(0x00);
-std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig;
+ for (const auto& record : data)
+ {
+ cipherList.push_back(record.value(cipher, 0));
+ }
-using namespace phosphor::logging;
-using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+ return cipherList;
+}
+} // namespace cipher
-namespace fs = std::filesystem;
-namespace variant_ns = sdbusplus::message::variant_ns;
+namespace ipmi
+{
+namespace transport
+{
-void register_netfn_transport_functions() __attribute__((constructor));
+// LAN Handler specific response codes
+constexpr Cc ccParamNotSupported = 0x80;
+constexpr Cc ccParamSetLocked = 0x81;
+constexpr Cc ccParamReadOnly = 0x82;
+
+// VLANs are a 12-bit value
+constexpr uint16_t VLAN_VALUE_MASK = 0x0fff;
+constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000;
+
+// Arbitrary v6 Address Limits to prevent too much output in ipmitool
+constexpr uint8_t MAX_IPV6_STATIC_ADDRESSES = 15;
+constexpr uint8_t MAX_IPV6_DYNAMIC_ADDRESSES = 15;
+
+// D-Bus Network Daemon definitions
+constexpr auto PATH_ROOT = "/xyz/openbmc_project/network";
+constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config";
+
+constexpr auto INTF_SYSTEMCONFIG =
+ "xyz.openbmc_project.Network.SystemConfiguration";
+constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface";
+constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP";
+constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create";
+constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress";
+constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
+constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
+ "xyz.openbmc_project.Network.Neighbor.CreateStatic";
+constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
+constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create";
+
+/** @brief Generic paramters for different address families */
+template <int family>
+struct AddrFamily
+{
+};
-struct ChannelConfig_t* getChannelConfig(int channel)
+/** @brief Parameter specialization for IPv4 */
+template <>
+struct AddrFamily<AF_INET>
{
- auto item = channelConfig.find(channel);
- if (item == channelConfig.end())
- {
- channelConfig[channel] = std::make_unique<struct ChannelConfig_t>();
- }
+ using addr = in_addr;
+ static constexpr auto protocol = IP::Protocol::IPv4;
+ static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
+ static constexpr uint8_t defaultPrefix = 32;
+ static constexpr char propertyGateway[] = "DefaultGateway";
+};
+
+/** @brief Parameter specialization for IPv6 */
+template <>
+struct AddrFamily<AF_INET6>
+{
+ using addr = in6_addr;
+ static constexpr auto protocol = IP::Protocol::IPv6;
+ static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
+ static constexpr uint8_t defaultPrefix = 128;
+ static constexpr char propertyGateway[] = "DefaultGateway6";
+};
+
+/** @brief Valid address origins for IPv4 */
+const std::unordered_set<IP::AddressOrigin> originsV4 = {
+ IP::AddressOrigin::Static,
+ IP::AddressOrigin::DHCP,
+};
+
+/** @brief Valid address origins for IPv6 */
+const std::unordered_set<IP::AddressOrigin> originsV6Static = {
+ IP::AddressOrigin::Static};
+const std::unordered_set<IP::AddressOrigin> originsV6Dynamic = {
+ IP::AddressOrigin::DHCP,
+ IP::AddressOrigin::SLAAC,
+};
+
+/** @brief Interface IP Address configuration parameters */
+template <int family>
+struct IfAddr
+{
+ std::string path;
+ typename AddrFamily<family>::addr address;
+ IP::AddressOrigin origin;
+ uint8_t prefix;
+};
+
+/** @brief Interface Neighbor configuration parameters */
+template <int family>
+struct IfNeigh
+{
+ std::string path;
+ typename AddrFamily<family>::addr ip;
+ ether_addr mac;
+};
- return channelConfig[channel].get();
+/** @brief IPMI LAN Parameters */
+enum class LanParam : uint8_t
+{
+ SetStatus = 0,
+ AuthSupport = 1,
+ AuthEnables = 2,
+ IP = 3,
+ IPSrc = 4,
+ MAC = 5,
+ SubnetMask = 6,
+ Gateway1 = 12,
+ Gateway1MAC = 13,
+ VLANId = 20,
+ CiphersuiteSupport = 22,
+ CiphersuiteEntries = 23,
+ IPFamilySupport = 50,
+ IPFamilyEnables = 51,
+ IPv6Status = 55,
+ IPv6StaticAddresses = 56,
+ IPv6DynamicAddresses = 59,
+ IPv6RouterControl = 64,
+ IPv6StaticRouter1IP = 65,
+ IPv6StaticRouter1MAC = 66,
+ IPv6StaticRouter1PrefixLength = 67,
+ IPv6StaticRouter1PrefixValue = 68,
+};
+
+static constexpr uint8_t oemCmdStart = 192;
+static constexpr uint8_t oemCmdEnd = 255;
+
+/** @brief IPMI IP Origin Types */
+enum class IPSrc : uint8_t
+{
+ Unspecified = 0,
+ Static = 1,
+ DHCP = 2,
+ BIOS = 3,
+ BMC = 4,
+};
+
+/** @brief IPMI Set Status */
+enum class SetStatus : uint8_t
+{
+ Complete = 0,
+ InProgress = 1,
+ Commit = 2,
+};
+
+/** @brief IPMI Family Suport Bits */
+namespace IPFamilySupportFlag
+{
+constexpr uint8_t IPv6Only = 0;
+constexpr uint8_t DualStack = 1;
+constexpr uint8_t IPv6Alerts = 2;
+} // namespace IPFamilySupportFlag
+
+/** @brief IPMI IPFamily Enables Flag */
+enum class IPFamilyEnables : uint8_t
+{
+ IPv4Only = 0,
+ IPv6Only = 1,
+ DualStack = 2,
+};
+
+/** @brief IPMI IPv6 Dyanmic Status Bits */
+namespace IPv6StatusFlag
+{
+constexpr uint8_t DHCP = 0;
+constexpr uint8_t SLAAC = 1;
+}; // namespace IPv6StatusFlag
+
+/** @brief IPMI IPv6 Source */
+enum class IPv6Source : uint8_t
+{
+ Static = 0,
+ SLAAC = 1,
+ DHCP = 2,
+};
+
+/** @brief IPMI IPv6 Address Status */
+enum class IPv6AddressStatus : uint8_t
+{
+ Active = 0,
+ Disabled = 1,
+};
+
+namespace IPv6RouterControlFlag
+{
+constexpr uint8_t Static = 0;
+constexpr uint8_t Dynamic = 1;
+}; // namespace IPv6RouterControlFlag
+
+/** @brief A trivial helper used to determine if two PODs are equal
+ *
+ * @params[in] a - The first object to compare
+ * @params[in] b - The second object to compare
+ * @return True if the objects are the same bytewise
+ */
+template <typename T>
+bool equal(const T& a, const T& b)
+{
+ static_assert(std::is_trivially_copyable_v<T>);
+ return std::memcmp(&a, &b, sizeof(T)) == 0;
}
-// Helper Function to get IP Address/NetMask/Gateway/MAC Address from Network
-// Manager or Cache based on Set-In-Progress State
-ipmi_ret_t getNetworkData(uint8_t lan_param, uint8_t* data, int channel)
+/** @brief Copies bytes from an array into a trivially copyable container
+ *
+ * @params[out] t - The container receiving the data
+ * @params[in] bytes - The data to copy
+ */
+template <size_t N, typename T>
+void copyInto(T& t, const std::array<uint8_t, N>& bytes)
{
- ipmi_ret_t rc = IPMI_CC_OK;
- sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+ static_assert(std::is_trivially_copyable_v<T>);
+ static_assert(N == sizeof(T));
+ std::memcpy(&t, bytes.data(), bytes.size());
+}
+
+/** @brief Gets a generic view of the bytes in the input container
+ *
+ * @params[in] t - The data to reference
+ * @return A string_view referencing the bytes in the container
+ */
+template <typename T>
+std::string_view dataRef(const T& t)
+{
+ static_assert(std::is_trivially_copyable_v<T>);
+ return {reinterpret_cast<const char*>(&t), sizeof(T)};
+}
- auto ethdevice = ipmi::getChannelName(channel);
- // if ethdevice is an empty string they weren't expecting this channel.
- if (ethdevice.empty())
+/** @brief The dbus parameters for the interface corresponding to a channel
+ * This helps reduce the number of mapper lookups we need for each
+ * query and simplifies finding the VLAN interface if needed.
+ */
+struct ChannelParams
+{
+ /** @brief The channel ID */
+ int id;
+ /** @brief channel name for the interface */
+ std::string ifname;
+ /** @brief Name of the service on the bus */
+ std::string service;
+ /** @brief Lower level adapter path that is guaranteed to not be a VLAN */
+ std::string ifPath;
+ /** @brief Logical adapter path used for address assignment */
+ std::string logicalPath;
+};
+
+/** @brief Determines the ethernet interface name corresponding to a channel
+ * Tries to map a VLAN object first so that the address information
+ * is accurate. Otherwise it gets the standard ethernet interface.
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] channel - The channel id corresponding to an ethernet interface
+ * @return Ethernet interface service and object path if it exists
+ */
+std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus,
+ uint8_t channel)
+{
+ auto ifname = getChannelName(channel);
+ if (ifname.empty())
{
- // TODO: return error from getNetworkData()
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return std::nullopt;
}
- auto ethIP = ethdevice + "/" + ipmi::network::IP_TYPE;
- auto channelConf = getChannelConfig(channel);
- try
+ // Enumerate all VLAN + ETHERNET interfaces
+ auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
+ "GetSubTree");
+ req.append(PATH_ROOT, 0,
+ std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
+ auto reply = bus.call(req);
+ ObjectTree objs;
+ reply.read(objs);
+
+ ChannelParams params;
+ for (const auto& [path, impls] : objs)
{
- switch (static_cast<LanParam>(lan_param))
+ if (path.find(ifname) == path.npos)
+ {
+ continue;
+ }
+ for (const auto& [service, intfs] : impls)
{
- case LanParam::IP:
+ bool vlan = false;
+ bool ethernet = false;
+ for (const auto& intf : intfs)
{
- std::string ipaddress;
- if (channelConf->lan_set_in_progress == SET_COMPLETE)
+ if (intf == INTF_VLAN)
{
- try
- {
- auto ipObjectInfo =
- ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE,
- ipmi::network::ROOT, ethIP);
-
- auto properties = ipmi::getAllDbusProperties(
- bus, ipObjectInfo.second, ipObjectInfo.first,
- ipmi::network::IP_INTERFACE);
-
- ipaddress =
- variant_ns::get<std::string>(properties["Address"]);
- }
- // ignore the exception, as it is a valid condition that
- // the system is not configured with any IP.
- catch (InternalFailure& e)
- {
- // nothing to do.
- }
+ vlan = true;
}
- else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
+ else if (intf == INTF_ETHERNET)
{
- ipaddress = channelConf->ipaddr;
+ ethernet = true;
}
-
- inet_pton(AF_INET, ipaddress.c_str(),
- reinterpret_cast<void*>(data));
}
- break;
-
- case LanParam::IPSRC:
+ if (params.service.empty() && (vlan || ethernet))
+ {
+ params.service = service;
+ }
+ if (params.ifPath.empty() && !vlan && ethernet)
+ {
+ params.ifPath = path;
+ }
+ if (params.logicalPath.empty() && vlan)
{
- std::string networkInterfacePath;
+ params.logicalPath = path;
+ }
+ }
+ }
- if (channelConf->lan_set_in_progress == SET_COMPLETE)
- {
- try
- {
- ipmi::ObjectTree ancestorMap;
- // if the system is having ip object,then
- // get the IP object.
- auto ipObject = ipmi::getDbusObject(
- bus, ipmi::network::IP_INTERFACE,
- ipmi::network::ROOT, ethIP);
-
- // Get the parent interface of the IP object.
- try
- {
- ipmi::InterfaceList interfaces;
- interfaces.emplace_back(
- ipmi::network::ETHERNET_INTERFACE);
-
- ancestorMap = ipmi::getAllAncestors(
- bus, ipObject.first, std::move(interfaces));
- }
- catch (InternalFailure& e)
- {
- // if unable to get the parent interface
- // then commit the error and return.
- log<level::ERR>(
- "Unable to get the parent interface",
- entry("PATH=%s", ipObject.first.c_str()),
- entry("INTERFACE=%s",
- ipmi::network::ETHERNET_INTERFACE));
- break;
- }
- // for an ip object there would be single parent
- // interface.
- networkInterfacePath = ancestorMap.begin()->first;
- }
- catch (InternalFailure& e)
- {
- // if there is no ip configured on the system,then
- // get the network interface object.
- auto networkInterfaceObject = ipmi::getDbusObject(
- bus, ipmi::network::ETHERNET_INTERFACE,
- ipmi::network::ROOT, ethdevice);
+ // We must have a path for the underlying interface
+ if (params.ifPath.empty())
+ {
+ return std::nullopt;
+ }
+ // We don't have a VLAN so the logical path is the same
+ if (params.logicalPath.empty())
+ {
+ params.logicalPath = params.ifPath;
+ }
- networkInterfacePath = networkInterfaceObject.first;
- }
+ params.id = channel;
+ params.ifname = std::move(ifname);
+ return std::move(params);
+}
- auto variant = ipmi::getDbusProperty(
- bus, ipmi::network::SERVICE, networkInterfacePath,
- ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled");
+/** @brief A trivial helper around maybeGetChannelParams() that throws an
+ * exception when it is unable to acquire parameters for the channel.
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] channel - The channel id corresponding to an ethernet interface
+ * @return Ethernet interface service and object path
+ */
+ChannelParams getChannelParams(sdbusplus::bus::bus& bus, uint8_t channel)
+{
+ auto params = maybeGetChannelParams(bus, channel);
+ if (!params)
+ {
+ log<level::ERR>("Failed to get channel params",
+ entry("CHANNEL=%" PRIu8, channel));
+ elog<InternalFailure>();
+ }
+ return std::move(*params);
+}
- auto dhcpEnabled = variant_ns::get<bool>(variant);
- // As per IPMI spec 2=>DHCP, 1=STATIC
- auto ipsrc = dhcpEnabled ? ipmi::network::IPOrigin::DHCP
- : ipmi::network::IPOrigin::STATIC;
+/** @brief Wraps the phosphor logging method to insert some additional metadata
+ *
+ * @param[in] params - The parameters for the channel
+ * ...
+ */
+template <auto level, typename... Args>
+auto logWithChannel(const ChannelParams& params, Args&&... args)
+{
+ return log<level>(std::forward<Args>(args)...,
+ entry("CHANNEL=%d", params.id),
+ entry("IFNAME=%s", params.ifname.c_str()));
+}
+template <auto level, typename... Args>
+auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args)
+{
+ if (params)
+ {
+ return logWithChannel<level>(*params, std::forward<Args>(args)...);
+ }
+ return log<level>(std::forward<Args>(args)...);
+}
- std::memcpy(data, &ipsrc, ipmi::network::IPSRC_SIZE_BYTE);
- }
- else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
- {
- std::memcpy(data, &(channelConf->ipsrc),
- ipmi::network::IPSRC_SIZE_BYTE);
- }
- }
- break;
+/** @brief Trivializes using parameter getter functions by providing a bus
+ * and channel parameters automatically.
+ *
+ * @param[in] channel - The channel id corresponding to an ethernet interface
+ * ...
+ */
+template <auto func, typename... Args>
+auto channelCall(uint8_t channel, Args&&... args)
+{
+ sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+ auto params = getChannelParams(bus, channel);
+ return std::invoke(func, bus, params, std::forward<Args>(args)...);
+}
- case LanParam::SUBNET:
- {
- unsigned long mask{};
- if (channelConf->lan_set_in_progress == SET_COMPLETE)
- {
- try
- {
- auto ipObjectInfo =
- ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE,
- ipmi::network::ROOT, ethIP);
-
- auto properties = ipmi::getAllDbusProperties(
- bus, ipObjectInfo.second, ipObjectInfo.first,
- ipmi::network::IP_INTERFACE);
-
- auto prefix = variant_ns::get<uint8_t>(
- properties["PrefixLength"]);
- mask = ipmi::network::MASK_32_BIT;
- mask = htonl(mask << (ipmi::network::BITS_32 - prefix));
- }
- // ignore the exception, as it is a valid condition that
- // the system is not configured with any IP.
- catch (InternalFailure& e)
- {
- // nothing to do
- }
- std::memcpy(data, &mask,
- ipmi::network::IPV4_ADDRESS_SIZE_BYTE);
- }
- else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
- {
- inet_pton(AF_INET, channelConf->netmask.c_str(),
- reinterpret_cast<void*>(data));
- }
- }
- break;
+/** @brief Determines if the ethernet interface is using DHCP
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @return True if DHCP is enabled, false otherwise
+ */
+bool getDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
+{
+ return std::get<bool>(getDbusProperty(
+ bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled"));
+}
- case LanParam::GATEWAY:
- {
- std::string gateway;
+/** @brief Sets the system value for DHCP on the given interface
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] on - Whether or not to enable DHCP
+ */
+void setDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ bool on)
+{
+ setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET,
+ "DHCPEnabled", on);
+}
- if (channelConf->lan_set_in_progress == SET_COMPLETE)
- {
- try
- {
- auto systemObject = ipmi::getDbusObject(
- bus, ipmi::network::SYSTEMCONFIG_INTERFACE,
- ipmi::network::ROOT);
+/** @brief Converts a human readable MAC string into MAC bytes
+ *
+ * @param[in] mac - The MAC string
+ * @return MAC in bytes
+ */
+ether_addr stringToMAC(const char* mac)
+{
+ const ether_addr* ret = ether_aton(mac);
+ if (ret == nullptr)
+ {
+ log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac));
+ elog<InternalFailure>();
+ }
+ return *ret;
+}
- auto systemProperties = ipmi::getAllDbusProperties(
- bus, systemObject.second, systemObject.first,
- ipmi::network::SYSTEMCONFIG_INTERFACE);
+/** @brief Determines the MAC of the ethernet interface
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @return The configured mac address
+ */
+ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
+{
+ auto macStr = std::get<std::string>(getDbusProperty(
+ bus, params.service, params.ifPath, INTF_MAC, "MACAddress"));
+ return stringToMAC(macStr.c_str());
+}
- gateway = variant_ns::get<std::string>(
- systemProperties["DefaultGateway"]);
- }
- // ignore the exception, as it is a valid condition that
- // the system is not configured with any IP.
- catch (InternalFailure& e)
- {
- // nothing to do
- }
- }
- else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
- {
- gateway = channelConf->gateway;
- }
+/** @brief Sets the system value for MAC address on the given interface
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] mac - MAC address to apply
+ */
+void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const ether_addr& mac)
+{
+ std::string macStr = ether_ntoa(&mac);
+ setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress",
+ macStr);
+}
- inet_pton(AF_INET, gateway.c_str(),
- reinterpret_cast<void*>(data));
- }
- break;
+/** @brief Turns an IP address string into the network byte order form
+ * NOTE: This version strictly validates family matches
+ *
+ * @param[in] address - The string form of the address
+ * @return A network byte order address or none if conversion failed
+ */
+template <int family>
+std::optional<typename AddrFamily<family>::addr>
+ maybeStringToAddr(const char* address)
+{
+ typename AddrFamily<family>::addr ret;
+ if (inet_pton(family, address, &ret) == 1)
+ {
+ return ret;
+ }
+ return std::nullopt;
+}
- case LanParam::MAC:
- {
- std::string macAddress;
- if (channelConf->lan_set_in_progress == SET_COMPLETE)
- {
- auto macObjectInfo =
- ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
- ipmi::network::ROOT, ethdevice);
+/** @brief Turns an IP address string into the network byte order form
+ * NOTE: This version strictly validates family matches
+ *
+ * @param[in] address - The string form of the address
+ * @return A network byte order address
+ */
+template <int family>
+typename AddrFamily<family>::addr stringToAddr(const char* address)
+{
+ auto ret = maybeStringToAddr<family>(address);
+ if (!ret)
+ {
+ log<level::ERR>("Failed to convert IP Address",
+ entry("FAMILY=%d", family),
+ entry("ADDRESS=%s", address));
+ elog<InternalFailure>();
+ }
+ return *ret;
+}
- auto variant = ipmi::getDbusProperty(
- bus, macObjectInfo.second, macObjectInfo.first,
- ipmi::network::MAC_INTERFACE, "MACAddress");
+/** @brief Turns an IP address in network byte order into a string
+ *
+ * @param[in] address - The string form of the address
+ * @return A network byte order address
+ */
+template <int family>
+std::string addrToString(const typename AddrFamily<family>::addr& address)
+{
+ std::string ret(AddrFamily<family>::maxStrLen, '\0');
+ inet_ntop(family, &address, ret.data(), ret.size());
+ ret.resize(strlen(ret.c_str()));
+ return ret;
+}
- macAddress = variant_ns::get<std::string>(variant);
- }
- else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
- {
- macAddress = channelConf->macAddress;
- }
+/** @brief Retrieves the current gateway for the address family on the system
+ * NOTE: The gateway is currently system wide and not per channel
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @return An address representing the gateway address if it exists
+ */
+template <int family>
+std::optional<typename AddrFamily<family>::addr>
+ getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
+{
+ auto gatewayStr = std::get<std::string>(getDbusProperty(
+ bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
+ AddrFamily<family>::propertyGateway));
+ if (gatewayStr.empty())
+ {
+ return std::nullopt;
+ }
+ return stringToAddr<family>(gatewayStr.c_str());
+}
- sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
- (data), (data + 1), (data + 2), (data + 3), (data + 4),
- (data + 5));
- }
- break;
+/** @brief A lazy lookup mechanism for iterating over object properties stored
+ * in DBus. This will only perform the object lookup when needed, and
+ * retains a cache of previous lookups to speed up future iterations.
+ */
+class ObjectLookupCache
+{
+ public:
+ using PropertiesCache = std::unordered_map<std::string, PropertyMap>;
+
+ /** @brief Creates a new ObjectLookupCache for the interface on the bus
+ * NOTE: The inputs to this object must outlive the object since
+ * they are only referenced by it.
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] intf - The interface we are looking up
+ */
+ ObjectLookupCache(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const char* intf) :
+ bus(bus),
+ params(params), intf(intf),
+ objs(getAllDbusObjects(bus, params.logicalPath, intf, ""))
+ {
+ }
- case LanParam::VLAN:
- {
- uint16_t vlanID{};
- if (channelConf->lan_set_in_progress == SET_COMPLETE)
- {
- try
- {
- auto ipObjectInfo = ipmi::getIPObject(
- bus, ipmi::network::IP_INTERFACE,
- ipmi::network::ROOT, ipmi::network::IP_TYPE);
+ class iterator : public ObjectTree::const_iterator
+ {
+ public:
+ using value_type = PropertiesCache::value_type;
- vlanID = static_cast<uint16_t>(
- ipmi::network::getVLAN(ipObjectInfo.first));
+ iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) :
+ ObjectTree::const_iterator(it), container(container),
+ ret(container.cache.end())
+ {
+ }
+ value_type& operator*()
+ {
+ ret = container.get(ObjectTree::const_iterator::operator*().first);
+ return *ret;
+ }
+ value_type* operator->()
+ {
+ return &operator*();
+ }
- vlanID = htole16(vlanID);
+ private:
+ ObjectLookupCache& container;
+ PropertiesCache::iterator ret;
+ };
- if (vlanID)
- {
- // Enable the 16th bit
- vlanID |= htole16(ipmi::network::VLAN_ENABLE_MASK);
- }
- }
- // ignore the exception, as it is a valid condition that
- // the system is not configured with any IP.
- catch (InternalFailure& e)
- {
- // nothing to do
- }
+ iterator begin() noexcept
+ {
+ return iterator(objs.begin(), *this);
+ }
- std::memcpy(data, &vlanID, ipmi::network::VLAN_SIZE_BYTE);
- }
- else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
- {
- std::memcpy(data, &(channelConf->vlanID),
- ipmi::network::VLAN_SIZE_BYTE);
- }
- }
- break;
+ iterator end() noexcept
+ {
+ return iterator(objs.end(), *this);
+ }
- default:
- rc = IPMI_CC_PARM_OUT_OF_RANGE;
+ private:
+ sdbusplus::bus::bus& bus;
+ const ChannelParams& params;
+ const char* const intf;
+ const ObjectTree objs;
+ PropertiesCache cache;
+
+ /** @brief Gets a cached copy of the object properties if possible
+ * Otherwise performs a query on DBus to look them up
+ *
+ * @param[in] path - The object path to lookup
+ * @return An iterator for the specified object path + properties
+ */
+ PropertiesCache::iterator get(const std::string& path)
+ {
+ auto it = cache.find(path);
+ if (it != cache.end())
+ {
+ return it;
}
+ auto properties = getAllDbusProperties(bus, params.service, path, intf);
+ return cache.insert({path, std::move(properties)}).first;
}
- catch (InternalFailure& e)
+};
+
+/** @brief Searches the ip object lookup cache for an address matching
+ * the input parameters. NOTE: The index lacks stability across address
+ * changes since the network daemon has no notion of stable indicies.
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] idx - The index of the desired address on the interface
+ * @param[in] origins - The allowed origins for the address objects
+ * @param[in] ips - The object lookup cache holding all of the address info
+ * @return The address and prefix if it was found
+ */
+template <int family>
+std::optional<IfAddr<family>>
+ findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ uint8_t idx,
+ const std::unordered_set<IP::AddressOrigin>& origins,
+ ObjectLookupCache& ips)
+{
+ for (const auto& [path, properties] : ips)
{
- commit<InternalFailure>();
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- return rc;
+ const auto& addrStr = std::get<std::string>(properties.at("Address"));
+ auto addr = maybeStringToAddr<family>(addrStr.c_str());
+ if (!addr)
+ {
+ continue;
+ }
+
+ IP::AddressOrigin origin = IP::convertAddressOriginFromString(
+ std::get<std::string>(properties.at("Origin")));
+ if (origins.find(origin) == origins.end())
+ {
+ continue;
+ }
+
+ if (idx > 0)
+ {
+ idx--;
+ continue;
+ }
+
+ IfAddr<family> ifaddr;
+ ifaddr.path = path;
+ ifaddr.address = *addr;
+ ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength"));
+ ifaddr.origin = origin;
+ return std::move(ifaddr);
}
- return rc;
+
+ return std::nullopt;
}
-namespace cipher
+/** @brief Trivial helper around findIfAddr that simplifies calls
+ * for one off lookups. Don't use this if you intend to do multiple
+ * lookups at a time.
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] idx - The index of the desired address on the interface
+ * @param[in] origins - The allowed origins for the address objects
+ * @return The address and prefix if it was found
+ */
+template <int family>
+auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ uint8_t idx,
+ const std::unordered_set<IP::AddressOrigin>& origins)
{
+ ObjectLookupCache ips(bus, params, INTF_IP);
+ return findIfAddr<family>(bus, params, idx, origins, ips);
+}
-std::vector<uint8_t> getCipherList()
+/** @brief Deletes the dbus object. Ignores empty objects or objects that are
+ * missing from the bus.
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] service - The name of the service
+ * @param[in] path - The path of the object to delete
+ */
+void deleteObjectIfExists(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& path)
{
- std::vector<uint8_t> cipherList;
-
- std::ifstream jsonFile(configFile);
- if (!jsonFile.is_open())
+ if (path.empty())
{
- log<level::ERR>("Channel Cipher suites file not found");
- elog<InternalFailure>();
+ return;
}
-
- auto data = Json::parse(jsonFile, nullptr, false);
- if (data.is_discarded())
+ try
{
- log<level::ERR>("Parsing channel cipher suites JSON failed");
- elog<InternalFailure>();
+ auto req = bus.new_method_call(service.c_str(), path.c_str(),
+ ipmi::DELETE_INTERFACE, "Delete");
+ bus.call_noreply(req);
}
-
- // Byte 1 is reserved
- cipherList.push_back(0x00);
-
- for (const auto& record : data)
+ catch (const sdbusplus::exception::SdBusError& e)
{
- cipherList.push_back(record.value(cipher, 0));
+ if (strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0)
+ {
+ // We want to rethrow real errors
+ throw;
+ }
}
+}
- return cipherList;
+/** @brief Sets the address info configured for the interface
+ * If a previous address path exists then it will be removed
+ * before the new address is added.
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] address - The address of the new IP
+ * @param[in] prefix - The prefix of the new IP
+ */
+template <int family>
+void createIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const typename AddrFamily<family>::addr& address,
+ uint8_t prefix)
+{
+ auto newreq =
+ bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
+ INTF_IP_CREATE, "IP");
+ std::string protocol =
+ sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
+ AddrFamily<family>::protocol);
+ newreq.append(protocol, addrToString<family>(address), prefix, "");
+ bus.call_noreply(newreq);
}
-} // namespace cipher
+/** @brief Trivial helper for getting the IPv4 address from getIfAddrs()
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @return The address and prefix if found
+ */
+auto getIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params)
+{
+ return getIfAddr<AF_INET>(bus, params, 0, originsV4);
+}
-ipmi_ret_t ipmi_transport_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief Reconfigures the IPv4 address info configured for the interface
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] address - The new address if specified
+ * @param[in] prefix - The new address prefix if specified
+ */
+void reconfigureIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const std::optional<in_addr>& address,
+ std::optional<uint8_t> prefix)
{
- // Status code.
- ipmi_ret_t rc = IPMI_CC_INVALID;
- *data_len = 0;
- return rc;
+ auto ifaddr = getIfAddr4(bus, params);
+ if (!ifaddr && !address)
+ {
+ log<level::ERR>("Missing address for IPv4 assignment");
+ elog<InternalFailure>();
+ }
+ uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix;
+ if (ifaddr)
+ {
+ fallbackPrefix = ifaddr->prefix;
+ deleteObjectIfExists(bus, params.service, ifaddr->path);
+ }
+ createIfAddr<AF_INET>(bus, params, address.value_or(ifaddr->address),
+ prefix.value_or(fallbackPrefix));
}
-struct set_lan_t
+template <int family>
+std::optional<IfNeigh<family>>
+ findStaticNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const typename AddrFamily<family>::addr& ip,
+ ObjectLookupCache& neighbors)
{
- uint8_t channel;
- uint8_t parameter;
- uint8_t data[8]; // Per IPMI spec, not expecting more than this size
-} __attribute__((packed));
+ const auto state =
+ sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
+ Neighbor::State::Permanent);
+ for (const auto& [path, neighbor] : neighbors)
+ {
+ const auto& ipStr = std::get<std::string>(neighbor.at("IPAddress"));
+ auto neighIP = maybeStringToAddr<family>(ipStr.c_str());
+ if (!neighIP)
+ {
+ continue;
+ }
+ if (!equal(*neighIP, ip))
+ {
+ continue;
+ }
+ if (state != std::get<std::string>(neighbor.at("State")))
+ {
+ continue;
+ }
+
+ IfNeigh<family> ret;
+ ret.path = path;
+ ret.ip = ip;
+ const auto& macStr = std::get<std::string>(neighbor.at("MACAddress"));
+ ret.mac = stringToMAC(macStr.c_str());
+ return std::move(ret);
+ }
+
+ return std::nullopt;
+}
-ipmi_ret_t checkAndUpdateNetwork(int channel)
+template <int family>
+void createNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const typename AddrFamily<family>::addr& address,
+ const ether_addr& mac)
{
- auto channelConf = getChannelConfig(channel);
- using namespace std::chrono_literals;
- // time to wait before applying the network changes.
- constexpr auto networkTimeout = 10000000us; // 10 sec
+ auto newreq =
+ bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
+ INTF_NEIGHBOR_CREATE_STATIC, "Neighbor");
+ std::string macStr = ether_ntoa(&mac);
+ newreq.append(addrToString<family>(address), macStr);
+ bus.call_noreply(newreq);
+}
- // Skip the timer. Expecting more update as we are in SET_IN_PROGRESS
- if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
+/** @brief Sets the system wide value for the default gateway
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] gateway - Gateway address to apply
+ */
+template <int family>
+void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const typename AddrFamily<family>::addr& address)
+{
+ // Save the old gateway MAC address if it exists so we can recreate it
+ auto gateway = getGatewayProperty<family>(bus, params);
+ std::optional<IfNeigh<family>> neighbor;
+ if (gateway)
{
- return IPMI_CC_OK;
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors);
}
- // Start the timer, if it is direct single param update without
- // SET_IN_PROGRESS or many params updated through SET_IN_PROGRESS to
- // SET_COMPLETE Note: Even for update with SET_IN_PROGRESS, don't apply the
- // changes immediately, as ipmitool sends each param individually
- // through SET_IN_PROGRESS to SET_COMPLETE.
- channelConf->flush = true;
- if (!networkTimer)
+ setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
+ AddrFamily<family>::propertyGateway,
+ addrToString<family>(address));
+
+ // Restore the gateway MAC if we had one
+ if (neighbor)
{
- log<level::ERR>("Network timer is not instantiated");
- return IPMI_CC_UNSPECIFIED_ERROR;
+ deleteObjectIfExists(bus, params.service, neighbor->path);
+ createNeighbor<family>(bus, params, address, neighbor->mac);
}
- // start the timer.
- networkTimer->start(networkTimeout);
- return IPMI_CC_OK;
}
-ipmi_ret_t ipmi_transport_set_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+template <int family>
+std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus::bus& bus,
+ const ChannelParams& params,
+ ObjectLookupCache& neighbors)
{
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = 0;
+ auto gateway = getGatewayProperty<family>(bus, params);
+ if (!gateway)
+ {
+ return std::nullopt;
+ }
- char ipaddr[INET_ADDRSTRLEN];
- char netmask[INET_ADDRSTRLEN];
- char gateway[INET_ADDRSTRLEN];
+ return findStaticNeighbor<family>(bus, params, *gateway, neighbors);
+}
- auto reqptr = reinterpret_cast<const set_lan_t*>(request);
- sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+template <int family>
+std::optional<IfNeigh<family>> getGatewayNeighbor(sdbusplus::bus::bus& bus,
+ const ChannelParams& params)
+{
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ return findGatewayNeighbor<family>(bus, params, neighbors);
+}
- // channel number is the lower nibble
- int channel = reqptr->channel & CHANNEL_MASK;
- auto ethdevice = ipmi::getChannelName(channel);
- if (ethdevice.empty())
+template <int family>
+void reconfigureGatewayMAC(sdbusplus::bus::bus& bus,
+ const ChannelParams& params, const ether_addr& mac)
+{
+ auto gateway = getGatewayProperty<family>(bus, params);
+ if (!gateway)
{
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ log<level::ERR>("Tried to set Gateway MAC without Gateway");
+ elog<InternalFailure>();
}
- auto channelConf = getChannelConfig(channel);
- switch (static_cast<LanParam>(reqptr->parameter))
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ auto neighbor =
+ findStaticNeighbor<family>(bus, params, *gateway, neighbors);
+ if (neighbor)
{
- case LanParam::IP:
- {
- std::snprintf(ipaddr, INET_ADDRSTRLEN,
- ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0],
- reqptr->data[1], reqptr->data[2], reqptr->data[3]);
+ deleteObjectIfExists(bus, params.service, neighbor->path);
+ }
- channelConf->ipaddr.assign(ipaddr);
- }
- break;
+ createNeighbor<family>(bus, params, *gateway, mac);
+}
- case LanParam::IPSRC:
- {
- uint8_t ipsrc{};
- std::memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE);
- channelConf->ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc);
- }
- break;
+/** @brief Deconfigures the IPv6 address info configured for the interface
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] idx - The address index to operate on
+ */
+void deconfigureIfAddr6(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ uint8_t idx)
+{
+ auto ifaddr = getIfAddr<AF_INET6>(bus, params, idx, originsV6Static);
+ if (ifaddr)
+ {
+ deleteObjectIfExists(bus, params.service, ifaddr->path);
+ }
+}
- case LanParam::MAC:
+/** @brief Reconfigures the IPv6 address info configured for the interface
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] idx - The address index to operate on
+ * @param[in] address - The new address
+ * @param[in] prefix - The new address prefix
+ */
+void reconfigureIfAddr6(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ uint8_t idx, const in6_addr& address, uint8_t prefix)
+{
+ deconfigureIfAddr6(bus, params, idx);
+ createIfAddr<AF_INET6>(bus, params, address, prefix);
+}
+
+/** @brief Converts the AddressOrigin into an IPv6Source
+ *
+ * @param[in] origin - The DBus Address Origin to convert
+ * @return The IPv6Source version of the origin
+ */
+IPv6Source originToSourceType(IP::AddressOrigin origin)
+{
+ switch (origin)
+ {
+ case IP::AddressOrigin::Static:
+ return IPv6Source::Static;
+ case IP::AddressOrigin::DHCP:
+ return IPv6Source::DHCP;
+ case IP::AddressOrigin::SLAAC:
+ return IPv6Source::SLAAC;
+ default:
{
- char mac[SIZE_MAC];
+ auto originStr = sdbusplus::xyz::openbmc_project::Network::server::
+ convertForMessage(origin);
+ log<level::ERR>(
+ "Invalid IP::AddressOrigin conversion to IPv6Source",
+ entry("ORIGIN=%s", originStr.c_str()));
+ elog<InternalFailure>();
+ }
+ }
+}
- std::snprintf(mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT,
- reqptr->data[0], reqptr->data[1], reqptr->data[2],
- reqptr->data[3], reqptr->data[4], reqptr->data[5]);
+/** @brief Packs the IPMI message response with IPv6 address data
+ *
+ * @param[out] ret - The IPMI response payload to be packed
+ * @param[in] channel - The channel id corresponding to an ethernet interface
+ * @param[in] set - The set selector for determining address index
+ * @param[in] origins - Set of valid origins for address filtering
+ */
+void getLanIPv6Address(message::Payload& ret, uint8_t channel, uint8_t set,
+ const std::unordered_set<IP::AddressOrigin>& origins)
+{
+ auto source = IPv6Source::Static;
+ bool enabled = false;
+ in6_addr addr{};
+ uint8_t prefix = AddrFamily<AF_INET6>::defaultPrefix;
+ auto status = IPv6AddressStatus::Disabled;
+
+ auto ifaddr = channelCall<getIfAddr<AF_INET6>>(channel, set, origins);
+ if (ifaddr)
+ {
+ source = originToSourceType(ifaddr->origin);
+ enabled = true;
+ addr = ifaddr->address;
+ prefix = ifaddr->prefix;
+ status = IPv6AddressStatus::Active;
+ }
- auto macObjectInfo =
- ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
- ipmi::network::ROOT, ethdevice);
+ ret.pack(set);
+ ret.pack(static_cast<uint4_t>(source), uint3_t{}, enabled);
+ ret.pack(std::string_view(reinterpret_cast<char*>(&addr), sizeof(addr)));
+ ret.pack(prefix);
+ ret.pack(static_cast<uint8_t>(status));
+}
- ipmi::setDbusProperty(
- bus, macObjectInfo.second, macObjectInfo.first,
- ipmi::network::MAC_INTERFACE, "MACAddress", std::string(mac));
+/** @brief Gets the vlan ID configured on the interface
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @return VLAN id or the standard 0 for no VLAN
+ */
+uint16_t getVLANProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
+{
+ // VLAN devices will always have a separate logical object
+ if (params.ifPath == params.logicalPath)
+ {
+ return 0;
+ }
- channelConf->macAddress = mac;
- }
- break;
+ auto vlan = std::get<uint32_t>(getDbusProperty(
+ bus, params.service, params.logicalPath, INTF_VLAN, "Id"));
+ if ((vlan & VLAN_VALUE_MASK) != vlan)
+ {
+ logWithChannel<level::ERR>(params, "networkd returned an invalid vlan",
+ entry("VLAN=%" PRIu32, vlan));
+ elog<InternalFailure>();
+ }
+ return vlan;
+}
- case LanParam::SUBNET:
+/** @brief Deletes all of the possible configuration parameters for a channel
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ */
+void deconfigureChannel(sdbusplus::bus::bus& bus, ChannelParams& params)
+{
+ // Delete all objects associated with the interface
+ auto objreq = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
+ "GetSubTree");
+ objreq.append(PATH_ROOT, 0, std::vector<std::string>{DELETE_INTERFACE});
+ auto objreply = bus.call(objreq);
+ ObjectTree objs;
+ objreply.read(objs);
+ for (const auto& [path, impls] : objs)
+ {
+ if (path.find(params.ifname) == path.npos)
{
- std::snprintf(netmask, INET_ADDRSTRLEN,
- ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0],
- reqptr->data[1], reqptr->data[2], reqptr->data[3]);
- channelConf->netmask.assign(netmask);
+ continue;
}
- break;
-
- case LanParam::GATEWAY:
+ for (const auto& [service, intfs] : impls)
{
- std::snprintf(gateway, INET_ADDRSTRLEN,
- ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0],
- reqptr->data[1], reqptr->data[2], reqptr->data[3]);
- channelConf->gateway.assign(gateway);
+ deleteObjectIfExists(bus, service, path);
}
- break;
-
- case LanParam::VLAN:
+ // Update params to reflect the deletion of vlan
+ if (path == params.logicalPath)
{
- uint16_t vlan{};
- std::memcpy(&vlan, reqptr->data, ipmi::network::VLAN_SIZE_BYTE);
- // We are not storing the enable bit
- // We assume that ipmitool always send enable
- // bit as 1.
- vlan = le16toh(vlan);
- channelConf->vlanID = vlan;
+ params.logicalPath = params.ifPath;
}
- break;
+ }
- case LanParam::INPROGRESS:
- {
- if (reqptr->data[0] == SET_COMPLETE)
- {
- channelConf->lan_set_in_progress = SET_COMPLETE;
+ // Clear out any settings on the lower physical interface
+ setDHCPProperty(bus, params, false);
+}
- log<level::INFO>(
- "Network data from Cache",
- entry("PREFIX=%s", channelConf->netmask.c_str()),
- entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
- entry("GATEWAY=%s", channelConf->gateway.c_str()),
- entry("VLAN=%d", channelConf->vlanID));
- }
- else if (reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress
- {
- channelConf->lan_set_in_progress = SET_IN_PROGRESS;
- }
- }
- break;
+/** @brief Creates a new VLAN on the specified interface
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] vlan - The id of the new vlan
+ */
+void createVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, uint16_t vlan)
+{
+ if (vlan == 0)
+ {
+ return;
+ }
- default:
+ auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT,
+ INTF_VLAN_CREATE, "VLAN");
+ req.append(params.ifname, static_cast<uint32_t>(vlan));
+ auto reply = bus.call(req);
+ sdbusplus::message::object_path newPath;
+ reply.read(newPath);
+ params.logicalPath = std::move(newPath);
+}
+
+/** @brief Performs the necessary reconfiguration to change the VLAN
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] vlan - The new vlan id to use
+ */
+void reconfigureVLAN(sdbusplus::bus::bus& bus, ChannelParams& params,
+ uint16_t vlan)
+{
+ // Unfortunatetly we don't have built-in functions to migrate our interface
+ // customizations to new VLAN interfaces, or have some kind of decoupling.
+ // We therefore must retain all of our old information, setup the new VLAN
+ // configuration, then restore the old info.
+
+ // Save info from the old logical interface
+ ObjectLookupCache ips(bus, params, INTF_IP);
+ auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips);
+ std::vector<IfAddr<AF_INET6>> ifaddrs6;
+ for (uint8_t i = 0; i < MAX_IPV6_STATIC_ADDRESSES; ++i)
+ {
+ auto ifaddr6 =
+ findIfAddr<AF_INET6>(bus, params, i, originsV6Static, ips);
+ if (!ifaddr6)
{
- rc = IPMI_CC_PARM_NOT_SUPPORTED;
- return rc;
+ break;
}
+ ifaddrs6.push_back(std::move(*ifaddr6));
}
- rc = checkAndUpdateNetwork(channel);
+ auto dhcp = getDHCPProperty(bus, params);
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors);
+ auto neighbor6 = findGatewayNeighbor<AF_INET6>(bus, params, neighbors);
+
+ deconfigureChannel(bus, params);
+ createVLAN(bus, params, vlan);
- return rc;
+ // Re-establish the saved settings
+ setDHCPProperty(bus, params, dhcp);
+ if (ifaddr4)
+ {
+ createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix);
+ }
+ for (const auto& ifaddr6 : ifaddrs6)
+ {
+ createIfAddr<AF_INET6>(bus, params, ifaddr6.address, ifaddr6.prefix);
+ }
+ if (neighbor4)
+ {
+ createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac);
+ }
+ if (neighbor6)
+ {
+ createNeighbor<AF_INET6>(bus, params, neighbor6->ip, neighbor6->mac);
+ }
}
-struct get_lan_t
+/** @brief Turns a prefix into a netmask
+ *
+ * @param[in] prefix - The prefix length
+ * @return The netmask
+ */
+in_addr prefixToNetmask(uint8_t prefix)
{
- uint8_t rev_channel;
- uint8_t parameter;
- uint8_t parameter_set;
- uint8_t parameter_block;
-} __attribute__((packed));
+ if (prefix > 32)
+ {
+ log<level::ERR>("Invalid prefix", entry("PREFIX=%" PRIu8, prefix));
+ elog<InternalFailure>();
+ }
+ if (prefix == 0)
+ {
+ // Avoids 32-bit lshift by 32 UB
+ return {};
+ }
+ return {htobe32(~UINT32_C(0) << (32 - prefix))};
+}
-ipmi_ret_t ipmi_transport_get_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief Turns a a netmask into a prefix length
+ *
+ * @param[in] netmask - The netmask in byte form
+ * @return The prefix length
+ */
+uint8_t netmaskToPrefix(in_addr netmask)
{
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = 0;
- const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0
-
- get_lan_t* reqptr = (get_lan_t*)request;
- // channel number is the lower nibble
- int channel = reqptr->rev_channel & CHANNEL_MASK;
+ uint32_t x = be32toh(netmask.s_addr);
+ if ((~x & (~x + 1)) != 0)
+ {
+ char maskStr[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &netmask, maskStr, sizeof(maskStr));
+ log<level::ERR>("Invalid netmask", entry("NETMASK=%s", maskStr));
+ elog<InternalFailure>();
+ }
+ return static_cast<bool>(x)
+ ? AddrFamily<AF_INET>::defaultPrefix - __builtin_ctz(x)
+ : 0;
+}
- if (reqptr->rev_channel & 0x80) // Revision is bit 7
+// We need to store this value so it can be returned to the client
+// It is volatile so safe to store in daemon memory.
+static std::unordered_map<uint8_t, SetStatus> setStatus;
+
+// Until we have good support for fixed versions of IPMI tool
+// we need to return the VLAN id for disabled VLANs. The value is only
+// used for verification that a disable operation succeeded and will only
+// be sent if our system indicates that vlans are disabled.
+static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan;
+
+/** @brief Gets the set status for the channel if it exists
+ * Otherise populates and returns the default value.
+ *
+ * @param[in] channel - The channel id corresponding to an ethernet interface
+ * @return A reference to the SetStatus for the channel
+ */
+SetStatus& getSetStatus(uint8_t channel)
+{
+ auto it = setStatus.find(channel);
+ if (it != setStatus.end())
{
- // Only current revision was requested
- *data_len = sizeof(current_revision);
- std::memcpy(response, &current_revision, *data_len);
- return IPMI_CC_OK;
+ return it->second;
}
+ return setStatus[channel] = SetStatus::Complete;
+}
- static std::vector<uint8_t> cipherList;
- static auto listInit = false;
+/**
+ * Define placeholder command handlers for the OEM Extension bytes for the Set
+ * LAN Configuration Parameters and Get LAN Configuration Parameters
+ * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem
+ * functions below to be overridden.
+ * To create handlers for your own proprietary command set:
+ * Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto
+ * recipe
+ * Create C++ file(s) that define IPMI handler functions matching the
+ * function names below (i.e. setLanOem). The default name for the
+ * transport IPMI commands is transporthandler_oem.cpp.
+ * Add:
+ * EXTRA_OECONF_append = " --enable-transport-oem=yes"
+ * Create a do_compile_prepend()/do_install_append method in your
+ * bbappend file to copy the file to the build directory.
+ * Add:
+ * PROJECT_SRC_DIR := "${THISDIR}/${PN}"
+ * # Copy the "strong" functions into the working directory, overriding the
+ * # placeholder functions.
+ * do_compile_prepend(){
+ * cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S}
+ * }
+ *
+ * # Clean up after complilation has completed
+ * do_install_append(){
+ * rm -f ${S}/transporthandler_oem.cpp
+ * }
+ *
+ */
+
+/**
+ * Define the placeholder OEM commands as having weak linkage. Create
+ * setLanOem, and getLanOem functions in the transporthandler_oem.cpp
+ * file. The functions defined there must not have the "weak" attribute
+ * applied to them.
+ */
+RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
+ __attribute__((weak));
+RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
+ uint8_t set, uint8_t block)
+ __attribute__((weak));
+
+RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
+{
+ req.trailingOk = true;
+ return response(ccParamNotSupported);
+}
- if (!listInit)
+RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
+ uint8_t set, uint8_t block)
+{
+ return response(ccParamNotSupported);
+}
+/**
+ * @brief is MAC address valid.
+ *
+ * This function checks whether the MAC address is valid or not.
+ *
+ * @param[in] mac - MAC address.
+ * @return true if MAC address is valid else retun false.
+ **/
+bool isValidMACAddress(const ether_addr& mac)
+{
+ // check if mac address is empty
+ if (equal(mac, ether_addr{}))
{
- try
- {
- cipherList = cipher::getCipherList();
- listInit = true;
- }
- catch (const std::exception& e)
- {
- return IPMI_CC_UNSPECIFIED_ERROR;
- }
+ return false;
+ }
+ // we accept only unicast MAC addresses and same thing has been checked in
+ // phosphor-network layer. If the least significant bit of the first octet
+ // is set to 1, it is multicast MAC else it is unicast MAC address.
+ if (mac.ether_addr_octet[0] & 1)
+ {
+ return false;
}
+ return true;
+}
- auto ethdevice = ipmi::getChannelName(channel);
- if (ethdevice.empty())
+RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter,
+ message::Payload& req)
+{
+ auto channel = static_cast<uint8_t>(channelBits);
+ if (!doesDeviceExist(channel))
{
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ req.trailingOk = true;
+ return responseInvalidFieldRequest();
}
- auto channelConf = getChannelConfig(channel);
- LanParam param = static_cast<LanParam>(reqptr->parameter);
- switch (param)
+ switch (static_cast<LanParam>(parameter))
{
- case LanParam::INPROGRESS:
+ case LanParam::SetStatus:
{
- uint8_t buf[] = {current_revision,
- channelConf->lan_set_in_progress};
- *data_len = sizeof(buf);
- std::memcpy(response, &buf, *data_len);
- break;
+ uint2_t flag;
+ uint6_t rsvd;
+ if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ if (rsvd)
+ {
+ return responseInvalidFieldRequest();
+ }
+ auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag));
+ switch (status)
+ {
+ case SetStatus::Complete:
+ {
+ getSetStatus(channel) = status;
+ return responseSuccess();
+ }
+ case SetStatus::InProgress:
+ {
+ auto& storedStatus = getSetStatus(channel);
+ if (storedStatus == SetStatus::InProgress)
+ {
+ return response(ccParamSetLocked);
+ }
+ storedStatus = status;
+ return responseSuccess();
+ }
+ case SetStatus::Commit:
+ if (getSetStatus(channel) != SetStatus::InProgress)
+ {
+ return responseInvalidFieldRequest();
+ }
+ return responseSuccess();
+ }
+ return response(ccParamNotSupported);
}
- case LanParam::AUTHSUPPORT:
+ case LanParam::AuthSupport:
{
- uint8_t buf[] = {current_revision, 0x04};
- *data_len = sizeof(buf);
- std::memcpy(response, &buf, *data_len);
- break;
+ req.trailingOk = true;
+ return response(ccParamReadOnly);
}
- case LanParam::AUTHENABLES:
+ case LanParam::AuthEnables:
{
- uint8_t buf[] = {current_revision, 0x04, 0x04, 0x04, 0x04, 0x04};
- *data_len = sizeof(buf);
- std::memcpy(response, &buf, *data_len);
- break;
+ req.trailingOk = true;
+ return response(ccParamReadOnly);
}
case LanParam::IP:
- case LanParam::SUBNET:
- case LanParam::GATEWAY:
- case LanParam::MAC:
{
- uint8_t buf[ipmi::network::MAC_ADDRESS_SIZE_BYTE + 1] = {};
-
- *data_len = sizeof(current_revision);
- std::memcpy(buf, &current_revision, *data_len);
-
- if (getNetworkData(reqptr->parameter, &buf[1], channel) ==
- IPMI_CC_OK)
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ return responseCommandNotAvailable();
+ }
+ in_addr ip;
+ std::array<uint8_t, sizeof(ip)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
{
- if (param == LanParam::MAC)
+ return responseReqDataLenInvalid();
+ }
+ copyInto(ip, bytes);
+ channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt);
+ return responseSuccess();
+ }
+ case LanParam::IPSrc:
+ {
+ uint4_t flag;
+ uint4_t rsvd;
+ if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ if (rsvd)
+ {
+ return responseInvalidFieldRequest();
+ }
+ switch (static_cast<IPSrc>(static_cast<uint8_t>(flag)))
+ {
+ case IPSrc::DHCP:
{
- *data_len = sizeof(buf);
+ channelCall<setDHCPProperty>(channel, true);
+ return responseSuccess();
}
- else
+ case IPSrc::Unspecified:
+ case IPSrc::Static:
+ case IPSrc::BIOS:
+ case IPSrc::BMC:
{
- *data_len = ipmi::network::IPV4_ADDRESS_SIZE_BYTE + 1;
+ channelCall<setDHCPProperty>(channel, false);
+ return responseSuccess();
}
- std::memcpy(response, &buf, *data_len);
}
- else
+ return response(ccParamNotSupported);
+ }
+ case LanParam::MAC:
+ {
+ ether_addr mac;
+ std::array<uint8_t, sizeof(mac)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
{
- rc = IPMI_CC_UNSPECIFIED_ERROR;
+ return responseReqDataLenInvalid();
}
- break;
+ copyInto(mac, bytes);
+
+ if (!isValidMACAddress(mac))
+ {
+ return responseInvalidFieldRequest();
+ }
+ channelCall<setMACProperty>(channel, mac);
+ return responseSuccess();
+ }
+ case LanParam::SubnetMask:
+ {
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ return responseCommandNotAvailable();
+ }
+ in_addr netmask;
+ std::array<uint8_t, sizeof(netmask)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(netmask, bytes);
+ channelCall<reconfigureIfAddr4>(channel, std::nullopt,
+ netmaskToPrefix(netmask));
+ return responseSuccess();
+ }
+ case LanParam::Gateway1:
+ {
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ return responseCommandNotAvailable();
+ }
+ in_addr gateway;
+ std::array<uint8_t, sizeof(gateway)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(gateway, bytes);
+ channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
+ return responseSuccess();
+ }
+ case LanParam::Gateway1MAC:
+ {
+ ether_addr gatewayMAC;
+ std::array<uint8_t, sizeof(gatewayMAC)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(gatewayMAC, bytes);
+ channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
+ return responseSuccess();
}
- case LanParam::VLAN:
+ case LanParam::VLANId:
{
- uint8_t buf[ipmi::network::VLAN_SIZE_BYTE + 1] = {};
+ uint12_t vlanData = 0;
+ uint3_t reserved = 0;
+ bool vlanEnable = 0;
- *data_len = sizeof(current_revision);
- std::memcpy(buf, &current_revision, *data_len);
- if (getNetworkData(reqptr->parameter, &buf[1], channel) ==
- IPMI_CC_OK)
+ if (req.unpack(vlanData) || req.unpack(reserved) ||
+ req.unpack(vlanEnable) || !req.fullyUnpacked())
{
- *data_len = sizeof(buf);
- std::memcpy(response, &buf, *data_len);
+ return responseReqDataLenInvalid();
}
- break;
+
+ if (reserved)
+ {
+ return responseInvalidFieldRequest();
+ }
+
+ uint16_t vlan = static_cast<uint16_t>(vlanData);
+
+ if (!vlanEnable)
+ {
+ lastDisabledVlan[channel] = vlan;
+ vlan = 0;
+ }
+ channelCall<reconfigureVLAN>(channel, vlan);
+
+ return responseSuccess();
}
- case LanParam::IPSRC:
+ case LanParam::CiphersuiteSupport:
+ case LanParam::CiphersuiteEntries:
+ case LanParam::IPFamilySupport:
{
- uint8_t buff[ipmi::network::IPSRC_SIZE_BYTE + 1] = {};
- *data_len = sizeof(current_revision);
- std::memcpy(buff, &current_revision, *data_len);
- if (getNetworkData(reqptr->parameter, &buff[1], channel) ==
- IPMI_CC_OK)
+ req.trailingOk = true;
+ return response(ccParamReadOnly);
+ }
+ case LanParam::IPFamilyEnables:
+ {
+ uint8_t enables;
+ if (req.unpack(enables) != 0 || !req.fullyUnpacked())
{
- *data_len = sizeof(buff);
- std::memcpy(response, &buff, *data_len);
+ return responseReqDataLenInvalid();
}
- break;
+ switch (static_cast<IPFamilyEnables>(enables))
+ {
+ case IPFamilyEnables::DualStack:
+ return responseSuccess();
+ case IPFamilyEnables::IPv4Only:
+ case IPFamilyEnables::IPv6Only:
+ return response(ccParamNotSupported);
+ }
+ return response(ccParamNotSupported);
}
- case LanParam::CIPHER_SUITE_COUNT:
+ case LanParam::IPv6Status:
{
- *(static_cast<uint8_t*>(response)) = current_revision;
- // Byte 1 is reserved byte and does not indicate a cipher suite ID,
- // so no of cipher suite entry count is one less than the size of
- // the vector
- auto count = static_cast<uint8_t>(cipherList.size() - 1);
- *(static_cast<uint8_t*>(response) + 1) = count;
- *data_len = sizeof(current_revision) + sizeof(count);
- break;
+ req.trailingOk = true;
+ return response(ccParamReadOnly);
}
- case LanParam::CIPHER_SUITE_ENTRIES:
+ case LanParam::IPv6StaticAddresses:
{
- *(static_cast<uint8_t*>(response)) = current_revision;
- // Byte 1 is reserved
- std::copy_n(cipherList.data(), cipherList.size(),
- static_cast<uint8_t*>(response) + 1);
- *data_len = sizeof(current_revision) +
- static_cast<uint8_t>(cipherList.size());
- break;
+ uint8_t set;
+ uint7_t rsvd;
+ bool enabled;
+ in6_addr ip;
+ std::array<uint8_t, sizeof(ip)> ipbytes;
+ uint8_t prefix;
+ uint8_t status;
+ if (req.unpack(set, rsvd, enabled, ipbytes, prefix, status) != 0 ||
+ !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ if (rsvd)
+ {
+ return responseInvalidFieldRequest();
+ }
+ copyInto(ip, ipbytes);
+ if (enabled)
+ {
+ channelCall<reconfigureIfAddr6>(channel, set, ip, prefix);
+ }
+ else
+ {
+ channelCall<deconfigureIfAddr6>(channel, set);
+ }
+ return responseSuccess();
+ }
+ case LanParam::IPv6DynamicAddresses:
+ {
+ req.trailingOk = true;
+ return response(ccParamReadOnly);
+ }
+ case LanParam::IPv6RouterControl:
+ {
+ std::bitset<8> control;
+ if (req.unpack(control) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ std::bitset<8> expected;
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ expected[IPv6RouterControlFlag::Dynamic] = 1;
+ }
+ else
+ {
+ expected[IPv6RouterControlFlag::Static] = 1;
+ }
+ if (expected != control)
+ {
+ return responseInvalidFieldRequest();
+ }
+ return responseSuccess();
+ }
+ case LanParam::IPv6StaticRouter1IP:
+ {
+ in6_addr gateway;
+ std::array<uint8_t, sizeof(gateway)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(gateway, bytes);
+ channelCall<setGatewayProperty<AF_INET6>>(channel, gateway);
+ return responseSuccess();
+ }
+ case LanParam::IPv6StaticRouter1MAC:
+ {
+ ether_addr mac;
+ std::array<uint8_t, sizeof(mac)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(mac, bytes);
+ channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac);
+ return responseSuccess();
+ }
+ case LanParam::IPv6StaticRouter1PrefixLength:
+ {
+ uint8_t prefix;
+ if (req.unpack(prefix) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ if (prefix != 0)
+ {
+ return responseInvalidFieldRequest();
+ }
+ return responseSuccess();
+ }
+ case LanParam::IPv6StaticRouter1PrefixValue:
+ {
+ std::array<uint8_t, sizeof(in6_addr)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ // Accept any prefix value since our prefix length has to be 0
+ return responseSuccess();
}
- default:
- log<level::ERR>("Unsupported parameter",
- entry("PARAMETER=0x%x", reqptr->parameter));
- rc = IPMI_CC_PARM_NOT_SUPPORTED;
}
- return rc;
+ if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
+ {
+ return setLanOem(channel, parameter, req);
+ }
+
+ req.trailingOk = true;
+ return response(ccParamNotSupported);
}
-void applyChanges(int channel)
+RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly,
+ uint8_t parameter, uint8_t set, uint8_t block)
{
- std::string ipaddress;
- std::string gateway;
- uint8_t prefix{};
- uint32_t vlanID{};
- std::string networkInterfacePath;
- ipmi::DbusObjectInfo ipObject;
- ipmi::DbusObjectInfo systemObject;
+ message::Payload ret;
+ constexpr uint8_t current_revision = 0x11;
+ ret.pack(current_revision);
- auto ethdevice = ipmi::getChannelName(channel);
- if (ethdevice.empty())
+ if (revOnly)
{
- log<level::ERR>("Unable to get the interface name",
- entry("CHANNEL=%d", channel));
- return;
+ return responseSuccess(std::move(ret));
}
- auto ethIp = ethdevice + "/" + ipmi::network::IP_TYPE;
- auto channelConf = getChannelConfig(channel);
- try
+ auto channel = static_cast<uint8_t>(channelBits);
+ if (!doesDeviceExist(channel))
{
- sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+ return responseInvalidFieldRequest();
+ }
- log<level::INFO>("Network data from Cache",
- entry("PREFIX=%s", channelConf->netmask.c_str()),
- entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
- entry("GATEWAY=%s", channelConf->gateway.c_str()),
- entry("VLAN=%d", channelConf->vlanID),
- entry("IPSRC=%d", channelConf->ipsrc));
- if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK)
+ static std::vector<uint8_t> cipherList;
+ static bool listInit = false;
+ if (!listInit)
+ {
+ try
{
- // get the first twelve bits which is vlan id
- // not interested in rest of the bits.
- channelConf->vlanID = le32toh(channelConf->vlanID);
- vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK;
+ cipherList = cipher::getCipherList();
+ listInit = true;
}
-
- // if the asked ip src is DHCP then not interested in
- // any given data except vlan.
- if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP)
+ catch (const std::exception& e)
{
- // always get the system object
- systemObject =
- ipmi::getDbusObject(bus, ipmi::network::SYSTEMCONFIG_INTERFACE,
- ipmi::network::ROOT);
+ }
+ }
- // the below code is to determine the mode of the interface
- // as the handling is same, if the system is configured with
- // DHCP or user has given all the data.
+ switch (static_cast<LanParam>(parameter))
+ {
+ case LanParam::SetStatus:
+ {
+ SetStatus status;
try
{
- ipmi::ObjectTree ancestorMap;
-
- ipmi::InterfaceList interfaces{
- ipmi::network::ETHERNET_INTERFACE};
-
- // if the system is having ip object,then
- // get the IP object.
- ipObject = ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE,
- ipmi::network::ROOT, ethIp);
-
- // Get the parent interface of the IP object.
- try
- {
- ancestorMap = ipmi::getAllAncestors(bus, ipObject.first,
- std::move(interfaces));
- }
- catch (InternalFailure& e)
- {
- // if unable to get the parent interface
- // then commit the error and return.
- log<level::ERR>("Unable to get the parent interface",
- entry("PATH=%s", ipObject.first.c_str()),
- entry("INTERFACE=%s",
- ipmi::network::ETHERNET_INTERFACE));
- commit<InternalFailure>();
- channelConf->clear();
- return;
- }
-
- networkInterfacePath = ancestorMap.begin()->first;
+ status = setStatus.at(channel);
}
- catch (InternalFailure& e)
+ catch (const std::out_of_range&)
{
- // TODO Currently IPMI supports single interface,need to handle
- // Multiple interface through
- // https://github.com/openbmc/openbmc/issues/2138
-
- // if there is no ip configured on the system,then
- // get the network interface object.
- auto networkInterfaceObject =
- ipmi::getDbusObject(bus, ipmi::network::ETHERNET_INTERFACE,
- ipmi::network::ROOT, ethdevice);
-
- networkInterfacePath = std::move(networkInterfaceObject.first);
+ status = SetStatus::Complete;
}
-
- // get the configured mode on the system.
- auto enableDHCP = variant_ns::get<bool>(ipmi::getDbusProperty(
- bus, ipmi::network::SERVICE, networkInterfacePath,
- ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled"));
-
- // if ip address source is not given then get the ip source mode
- // from the system so that it can be applied later.
- if (channelConf->ipsrc == ipmi::network::IPOrigin::UNSPECIFIED)
+ ret.pack(static_cast<uint2_t>(status), uint6_t{});
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::AuthSupport:
+ {
+ std::bitset<6> support;
+ ret.pack(support, uint2_t{});
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::AuthEnables:
+ {
+ std::bitset<6> enables;
+ ret.pack(enables, uint2_t{}); // Callback
+ ret.pack(enables, uint2_t{}); // User
+ ret.pack(enables, uint2_t{}); // Operator
+ ret.pack(enables, uint2_t{}); // Admin
+ ret.pack(enables, uint2_t{}); // OEM
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IP:
+ {
+ auto ifaddr = channelCall<getIfAddr4>(channel);
+ in_addr addr{};
+ if (ifaddr)
{
- channelConf->ipsrc = (enableDHCP)
- ? ipmi::network::IPOrigin::DHCP
- : ipmi::network::IPOrigin::STATIC;
+ addr = ifaddr->address;
}
-
- // check whether user has given all the data
- // or the configured system interface is dhcp enabled,
- // in both of the cases get the values from the cache.
- if ((!channelConf->ipaddr.empty() &&
- !channelConf->netmask.empty() &&
- !channelConf->gateway.empty()) ||
- (enableDHCP)) // configured system interface mode = DHCP
+ ret.pack(dataRef(addr));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPSrc:
+ {
+ auto src = IPSrc::Static;
+ if (channelCall<getDHCPProperty>(channel))
{
- // convert mask into prefix
- ipaddress = channelConf->ipaddr;
- prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask);
- gateway = channelConf->gateway;
+ src = IPSrc::DHCP;
}
- else // asked ip src = static and configured system src = static
- // or partially given data.
+ ret.pack(static_cast<uint4_t>(src), uint4_t{});
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::MAC:
+ {
+ ether_addr mac = channelCall<getMACProperty>(channel);
+ ret.pack(dataRef(mac));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::SubnetMask:
+ {
+ auto ifaddr = channelCall<getIfAddr4>(channel);
+ uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
+ if (ifaddr)
{
- // We have partial filled cache so get the remaining
- // info from the system.
-
- // Get the network data from the system as user has
- // not given all the data then use the data fetched from the
- // system but it is implementation dependent,IPMI spec doesn't
- // force it.
-
- // if system is not having any ip object don't throw error,
- try
- {
- auto properties = ipmi::getAllDbusProperties(
- bus, ipObject.second, ipObject.first,
- ipmi::network::IP_INTERFACE);
-
- ipaddress = channelConf->ipaddr.empty()
- ? variant_ns::get<std::string>(
- properties["Address"])
- : channelConf->ipaddr;
-
- prefix = channelConf->netmask.empty()
- ? variant_ns::get<uint8_t>(
- properties["PrefixLength"])
- : ipmi::network::toPrefix(
- AF_INET, channelConf->netmask);
- }
- catch (InternalFailure& e)
- {
- log<level::INFO>(
- "Failed to get IP object which matches",
- entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
- entry("MATCH=%s", ethIp.c_str()));
- }
-
- auto systemProperties = ipmi::getAllDbusProperties(
- bus, systemObject.second, systemObject.first,
- ipmi::network::SYSTEMCONFIG_INTERFACE);
-
- gateway = channelConf->gateway.empty()
- ? variant_ns::get<std::string>(
- systemProperties["DefaultGateway"])
- : channelConf->gateway;
+ prefix = ifaddr->prefix;
}
+ in_addr netmask = prefixToNetmask(prefix);
+ ret.pack(dataRef(netmask));
+ return responseSuccess(std::move(ret));
}
-
- // Currently network manager doesn't support purging of all the
- // ip addresses and the vlan interfaces from the parent interface,
- // TODO once the support is there, will make the change here.
- // https://github.com/openbmc/openbmc/issues/2141.
-
- // TODO Currently IPMI supports single interface,need to handle
- // Multiple interface through
- // https://github.com/openbmc/openbmc/issues/2138
-
- // instead of deleting all the vlan interfaces and
- // all the ipv4 address,we will call reset method.
- // delete all the vlan interfaces
-
- ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT,
- ipmi::network::VLAN_INTERFACE);
-
- // set the interface mode to static
- auto networkInterfaceObject =
- ipmi::getDbusObject(bus, ipmi::network::ETHERNET_INTERFACE,
- ipmi::network::ROOT, ethdevice);
-
- // setting the physical interface mode to static.
- ipmi::setDbusProperty(
- bus, ipmi::network::SERVICE, networkInterfaceObject.first,
- ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false);
-
- networkInterfacePath = networkInterfaceObject.first;
-
- // delete all the ipv4 addresses
- ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT,
- ipmi::network::IP_INTERFACE, ethIp);
-
- if (vlanID)
+ case LanParam::Gateway1:
{
- ipmi::network::createVLAN(bus, ipmi::network::SERVICE,
- ipmi::network::ROOT, ethdevice, vlanID);
-
- auto networkInterfaceObject = ipmi::getDbusObject(
- bus, ipmi::network::VLAN_INTERFACE, ipmi::network::ROOT);
-
- networkInterfacePath = networkInterfaceObject.first;
+ auto gateway =
+ channelCall<getGatewayProperty<AF_INET>>(channel).value_or(
+ in_addr{});
+ ret.pack(dataRef(gateway));
+ return responseSuccess(std::move(ret));
}
-
- if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP)
+ case LanParam::Gateway1MAC:
{
- ipmi::setDbusProperty(
- bus, ipmi::network::SERVICE, networkInterfacePath,
- ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", true);
+ ether_addr mac{};
+ auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
+ if (neighbor)
+ {
+ mac = neighbor->mac;
+ }
+ ret.pack(dataRef(mac));
+ return responseSuccess(std::move(ret));
}
- else
+ case LanParam::VLANId:
{
- // change the mode to static
- ipmi::setDbusProperty(
- bus, ipmi::network::SERVICE, networkInterfacePath,
- ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false);
-
- if (!ipaddress.empty())
+ uint16_t vlan = channelCall<getVLANProperty>(channel);
+ if (vlan != 0)
{
- ipmi::network::createIP(bus, ipmi::network::SERVICE,
- networkInterfacePath, ipv4Protocol,
- ipaddress, prefix);
+ vlan |= VLAN_ENABLE_FLAG;
}
-
- if (!gateway.empty())
+ else
{
- ipmi::setDbusProperty(bus, systemObject.second,
- systemObject.first,
- ipmi::network::SYSTEMCONFIG_INTERFACE,
- "DefaultGateway", std::string(gateway));
+ vlan = lastDisabledVlan[channel];
}
+ ret.pack(vlan);
+ return responseSuccess(std::move(ret));
}
- }
- catch (sdbusplus::exception::exception& e)
- {
- log<level::ERR>(
- "Failed to set network data", entry("PREFIX=%d", prefix),
- entry("ADDRESS=%s", ipaddress.c_str()),
- entry("GATEWAY=%s", gateway.c_str()), entry("VLANID=%d", vlanID),
- entry("IPSRC=%d", channelConf->ipsrc));
-
- commit<InternalFailure>();
- }
-
- channelConf->clear();
-}
-
-void commitNetworkChanges()
-{
- for (const auto& channel : channelConfig)
- {
- if (channel.second->flush)
+ case LanParam::CiphersuiteSupport:
+ {
+ if (!listInit)
+ {
+ return responseUnspecifiedError();
+ }
+ ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::CiphersuiteEntries:
+ {
+ if (!listInit)
+ {
+ return responseUnspecifiedError();
+ }
+ ret.pack(cipherList);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPFamilySupport:
+ {
+ std::bitset<8> support;
+ support[IPFamilySupportFlag::IPv6Only] = 0;
+ support[IPFamilySupportFlag::DualStack] = 1;
+ support[IPFamilySupportFlag::IPv6Alerts] = 1;
+ ret.pack(support);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPFamilyEnables:
+ {
+ ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6Status:
{
- applyChanges(channel.first);
+ ret.pack(MAX_IPV6_STATIC_ADDRESSES);
+ ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES);
+ std::bitset<8> support;
+ support[IPv6StatusFlag::DHCP] = 1;
+ support[IPv6StatusFlag::SLAAC] = 1;
+ ret.pack(support);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticAddresses:
+ {
+ if (set >= MAX_IPV6_STATIC_ADDRESSES)
+ {
+ return responseParmOutOfRange();
+ }
+ getLanIPv6Address(ret, channel, set, originsV6Static);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6DynamicAddresses:
+ {
+ if (set >= MAX_IPV6_DYNAMIC_ADDRESSES)
+ {
+ return responseParmOutOfRange();
+ }
+ getLanIPv6Address(ret, channel, set, originsV6Dynamic);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6RouterControl:
+ {
+ std::bitset<8> control;
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ control[IPv6RouterControlFlag::Dynamic] = 1;
+ }
+ else
+ {
+ control[IPv6RouterControlFlag::Static] = 1;
+ }
+ ret.pack(control);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticRouter1IP:
+ {
+ in6_addr gateway{};
+ if (!channelCall<getDHCPProperty>(channel))
+ {
+ gateway =
+ channelCall<getGatewayProperty<AF_INET6>>(channel).value_or(
+ in6_addr{});
+ }
+ ret.pack(dataRef(gateway));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticRouter1MAC:
+ {
+ ether_addr mac{};
+ auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel);
+ if (neighbor)
+ {
+ mac = neighbor->mac;
+ }
+ ret.pack(dataRef(mac));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticRouter1PrefixLength:
+ {
+ ret.pack(UINT8_C(0));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticRouter1PrefixValue:
+ {
+ in6_addr prefix{};
+ ret.pack(dataRef(prefix));
+ return responseSuccess(std::move(ret));
}
}
-}
-void createNetworkTimer()
-{
- if (!networkTimer)
+ if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
{
- std::function<void()> networkTimerCallback(
- std::bind(&commitNetworkChanges));
-
- networkTimer = std::make_unique<phosphor::Timer>(networkTimerCallback);
+ return getLanOem(channel, parameter, set, block);
}
-}
-void register_netfn_transport_functions()
-{
- // As this timer is only for transport handler
- // so creating it here.
- createNetworkTimer();
- // <Wildcard Command>
- ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_WILDCARD, NULL,
- ipmi_transport_wildcard, PRIVILEGE_USER);
+ return response(ccParamNotSupported);
+}
- // <Set LAN Configuration Parameters>
- ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_SET_LAN, NULL,
- ipmi_transport_set_lan, PRIVILEGE_ADMIN);
+} // namespace transport
+} // namespace ipmi
- // <Get LAN Configuration Parameters>
- ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_GET_LAN, NULL,
- ipmi_transport_get_lan, PRIVILEGE_OPERATOR);
+void register_netfn_transport_functions() __attribute__((constructor));
- return;
+void register_netfn_transport_functions()
+{
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
+ ipmi::transport::cmdSetLanConfigParameters,
+ ipmi::Privilege::Admin, ipmi::transport::setLan);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
+ ipmi::transport::cmdGetLanConfigParameters,
+ ipmi::Privilege::Operator, ipmi::transport::getLan);
}
diff --git a/transporthandler.hpp b/transporthandler.hpp
deleted file mode 100644
index 04d4673..0000000
--- a/transporthandler.hpp
+++ /dev/null
@@ -1,138 +0,0 @@
-#pragma once
-
-#include <ipmid/types.hpp>
-#include <string>
-// IPMI commands for Transport net functions.
-enum ipmi_netfn_storage_cmds
-{
- // Get capability bits
- IPMI_CMD_SET_LAN = 0x01,
- IPMI_CMD_GET_LAN = 0x02,
-};
-
-// Command specific completion codes
-enum ipmi_transport_return_codes
-{
- IPMI_CC_PARM_NOT_SUPPORTED = 0x80,
-};
-
-// Parameters
-enum class LanParam : uint8_t
-{
- INPROGRESS = 0,
- AUTHSUPPORT = 1, // Read-only
- AUTHENABLES = 2,
- IP = 3,
- IPSRC = 4,
- MAC = 5,
- SUBNET = 6,
- IPHEADER_PARAMS = 7,
- RMCP_PORT = 8,
- RMCP_SECONDARY_PORT = 9,
- BMC_GENERATED_ARP_CTRL = 10,
- GRATUITOUS_ARP_INTERVAL = 11,
- GATEWAY = 12,
- GATEWAY_MAC = 13,
- GATEWAY_BACKUP = 14,
- GATEWAY_BACKUP_MAC = 15,
- COMMUNITY_STRING = 16,
- LAN_ALERT_DESTINATION_COUNT = 17, // Read-only
- LAN_ALERT_DESTINATION_TYPE = 18, // Type per destination
- LAN_ALERT_DESTINATIONS = 19,
- VLAN = 20,
- VLAN_PRIORITY = 21,
- CIPHER_SUITE_COUNT = 22, // Read-only
- CIPHER_SUITE_ENTRIES = 23, // Read-only
- CIPHER_SUITE_PRIVILEGE_LEVELS = 24,
- DESTINATION_ADDR_VLAN_TAGS = 25,
- BAD_PASSWORD_THRESHOLD = 26,
- IPV6_AND_IPV4_SUPPORTED = 50, // Read-only
- IPV6_AND_IPV4_ENABLES = 51,
- IPV6_HEADER_STATIC_TRAFFIC_CLASS = 52,
- IPV6_HEADER_STATIC_HOP_LIMIT = 53,
- IPV6_HEADER_FLOW_LABEL = 54,
- IPV6_STATUS = 55, // Read-only
- IPV6_STATIC_ADDRESSES = 56,
- IPV6_DHCPV6_STATIC_DUID_STORAGE_LENGTH = 57, // Read-only
- IPV6_DHCPV6_STATIC_DUIDS = 58,
- IPV6_DYNAMIC_ADDRESSES = 59, // Read-only
- IPV6_DHCPV6_DYNAMIC_DUID_STOR_LEN = 60, // Read-only
- IPV6_DHCPV6_DYNAMIC_DUIDS = 61,
- IPV6_DHCPV6_TIMING_CONF_SUPPORT = 62, // Read-only
- IPV6_DHCPV6_TIMING_CONFIGURATION = 63,
- IPV6_ROUTER_ADDRESS_CONF_CTRL = 64,
- IPV6_STATIC_ROUTER_1_IP_ADDR = 65,
- IPV6_STATIC_ROUTER_1_MAC_ADDR = 66,
- IPV6_STATIC_ROUTER_1_PREFIX_LEN = 67,
- IPV6_STATIC_ROUTER_1_PREFIX_VAL = 68,
- IPV6_STATIC_ROUTER_2_IP_ADDR = 69,
- IPV6_STATIC_ROUTER_2_MAC_ADDR = 70,
- IPV6_STATIC_ROUTER_2_PREFIX_LEN = 71,
- IPV6_STATIC_ROUTER_2_PREFIX_VAL = 72,
- DYNAMIC_ROUTER_INFO_SET_COUNT = 73, // Read-only
- IPV6_DYNAMIC_ROUTER_INFO_IP_ADDR = 74, // Read-only
- IPV6_DYNAMIC_ROUTER_INFO_MAC = 75, // Read-only
- IPV6_DYNAMIC_ROUTER_INFO_PREFIX_LEN = 76, // Read-only
- IPV6_DYNAMIC_ROUTER_INFO_PREFIX_VAL = 77, // Read-only
- IPV6_DYNAMIC_ROUTER_RECV_HOP_LIMIT = 78,
- IPV6_NEIGHBOR_TIMING_CONF_SUPPORT = 79, // Read-only
- IPV6_NEIGHBOR_TIMING_CONFIGURATION = 80,
-};
-
-constexpr uint8_t SET_COMPLETE = 0;
-constexpr uint8_t SET_IN_PROGRESS = 1;
-constexpr uint8_t SET_COMMIT_WRITE = 2; // Optional
-constexpr uint8_t SET_IN_PROGRESS_RESERVED = 3; // Reserved
-
-const int CHANNEL_MASK = 0x0f;
-const int NUM_CHANNELS = 0x0f;
-
-struct ChannelConfig_t
-{
- std::string ipaddr;
- ipmi::network::IPOrigin ipsrc = ipmi::network::IPOrigin::UNSPECIFIED;
- std::string netmask;
- std::string gateway;
- std::string macAddress;
- // IPMI stores the vlan info in 16 bits,32 bits is to aligned
- // with phosphor-dbus interfaces.
- // vlan id is in 12 bits and the 16th bit is for enable mask.
- uint32_t vlanID = ipmi::network::VLAN_ID_MASK;
- uint8_t lan_set_in_progress = SET_COMPLETE;
- bool flush = false;
-
- void clear()
- {
- ipaddr.clear();
- netmask.clear();
- gateway.clear();
- macAddress.clear();
- vlanID = ipmi::network::VLAN_ID_MASK;
- ipsrc = ipmi::network::IPOrigin::UNSPECIFIED;
- lan_set_in_progress = SET_COMPLETE;
- flush = false;
- }
-};
-
-// Given a channel, get the corresponding configuration,
-// or allocate it first.
-//
-// @param[in] channel the channel
-// @return the ChannelConfig_t pointer.
-struct ChannelConfig_t* getChannelConfig(int channel);
-
-/** @brief Iterate over all the channelconfig and if
- * user has given the data for a channel then
- * apply the network changes for that channel.
- */
-void commitNetworkChanges();
-
-/* @brief Apply the network changes which is there in the
- * network cache for a given channel which gets filled
- * through setLan command. If some of the network
- * parameter was not given by the setLan then this function
- * gets the value of that parameter which is already
- * configured on the system.
- * @param[in] channel: channel number.
- */
-void applyChanges(int channel);
diff --git a/user_channel/Makefile.am b/user_channel/Makefile.am
index 3860a39..747c4c8 100644
--- a/user_channel/Makefile.am
+++ b/user_channel/Makefile.am
@@ -12,7 +12,12 @@ COMMON_CXX = \
-DBOOST_ASIO_DISABLE_THREADS \
-DBOOST_ALL_NO_LIB
-lib_LTLIBRARIES = libuserlayer.la libchannellayer.la
+
+lib_LTLIBRARIES =
+
+if FEATURE_LIBUSERLAYER
+
+lib_LTLIBRARIES += libuserlayer.la
libuserlayer_la_SOURCES = \
user_layer.cpp \
user_mgmt.cpp \
@@ -29,7 +34,9 @@ libuserlayer_la_LDFLAGS = \
libuserlayer_la_CXXFLAGS = \
-I$(top_srcdir) \
$(COMMON_CXX)
+endif
+lib_LTLIBRARIES += libchannellayer.la
libchannellayer_la_SOURCES = \
channel_mgmt.cpp \
channel_layer.cpp
diff --git a/user_channel/channel_layer.cpp b/user_channel/channel_layer.cpp
index 34a596d..c6866c2 100644
--- a/user_channel/channel_layer.cpp
+++ b/user_channel/channel_layer.cpp
@@ -58,11 +58,6 @@ bool isValidChannel(const uint8_t chNum)
return getChannelConfigObject().isValidChannel(chNum);
}
-uint8_t convertCurrentChannelNum(const uint8_t chNum)
-{
- return getChannelConfigObject().convertToChannelIndexNumber(chNum);
-}
-
bool isValidAuthType(const uint8_t chNum, const EAuthType& authType)
{
return getChannelConfigObject().isValidAuthType(chNum, authType);
@@ -142,4 +137,19 @@ std::string getChannelName(const uint8_t chNum)
return getChannelConfigObject().getChannelName(chNum);
}
+uint8_t getChannelByName(const std::string& chName)
+{
+ return getChannelConfigObject().getChannelByName(chName);
+}
+
+bool isValidPayloadType(const PayloadType payloadType)
+{
+ return (
+ payloadType == PayloadType::IPMI || payloadType == PayloadType::SOL ||
+ payloadType == PayloadType::OPEN_SESSION_REQUEST ||
+ payloadType == PayloadType::OPEN_SESSION_RESPONSE ||
+ payloadType == PayloadType::RAKP1 ||
+ payloadType == PayloadType::RAKP2 ||
+ payloadType == PayloadType::RAKP3 || payloadType == PayloadType::RAKP4);
+}
} // namespace ipmi
diff --git a/user_channel/channel_layer.hpp b/user_channel/channel_layer.hpp
index 1a8d64c..4eb51b7 100644
--- a/user_channel/channel_layer.hpp
+++ b/user_channel/channel_layer.hpp
@@ -16,7 +16,6 @@
#pragma once
#include <ipmid/api.h>
-#include <ipmid/message.hpp>
#include <string>
namespace ipmi
@@ -24,6 +23,7 @@ namespace ipmi
static constexpr uint8_t maxIpmiChannels = 16;
static constexpr uint8_t currentChNum = 0xE;
+static constexpr uint8_t invalidChannel = 0xff;
/**
* @enum IPMI return codes specific to channel (refer spec se 22.22 response
@@ -280,30 +280,21 @@ ipmi_ret_t getChannelAccessData(const uint8_t chNum,
/** @brief provides function to convert current channel number (0xE)
*
* @param[in] chNum - channel number as requested in commands.
- * @param[in] ipmi::context - ipmi context ptr, which has more details
+ * @param[in] devChannel - channel number as provided by device (not 0xE)
*
* @return same channel number or proper channel number for current channel
* number (0xE).
*/
-inline uint8_t convertCurrentChannelNum(const uint8_t chNum,
- ipmi::Context::ptr ctx)
+static inline uint8_t convertCurrentChannelNum(const uint8_t chNum,
+ const uint8_t devChannel)
{
if (chNum == currentChNum)
{
- return ctx->channel;
+ return devChannel;
}
return chNum;
}
-/** @brief provides function to convert current channel number (0xE)
- *
- * @param[in] chNum - channel number as requested in commands.
- *
- * @return same channel number or proper channel number for current channel
- * number (0xE).
- */
-uint8_t convertCurrentChannelNum(const uint8_t chNum);
-
/** @brief to set channel access data
*
* @param[in] chNum - channel number
@@ -367,4 +358,20 @@ ipmi_ret_t getChannelEnabledAuthType(const uint8_t chNum, const uint8_t priv,
*/
std::string getChannelName(const uint8_t chNum);
+/** @brief Retrieves the LAN channel number from the IPMI channel name
+ *
+ * @param[in] chName - IPMI channel name (i.e. eth0)
+ *
+ * @return the LAN channel number
+ */
+uint8_t getChannelByName(const std::string& chName);
+
+/** @brief determines whether payload type is valid
+ *
+ * @param[in] payload type - Payload Type
+ *
+ * @return true if valid, false otherwise
+ */
+bool isValidPayloadType(const PayloadType payloadType);
+
} // namespace ipmi
diff --git a/user_channel/channel_mgmt.cpp b/user_channel/channel_mgmt.cpp
index 3fb19b2..759de43 100644
--- a/user_channel/channel_mgmt.cpp
+++ b/user_channel/channel_mgmt.cpp
@@ -34,7 +34,6 @@
namespace ipmi
{
-namespace variant_ns = sdbusplus::message::variant_ns;
using namespace phosphor::logging;
static constexpr const char* channelAccessDefaultFilename =
@@ -143,7 +142,7 @@ std::string ChannelConfig::getChannelName(const uint8_t chNum)
if (!isValidChannel(chNum))
{
log<level::ERR>("Invalid channel number.",
- entry("ChannelID:%d", chNum));
+ entry("CHANNEL_ID=%d", chNum));
throw std::invalid_argument("Invalid channel number");
}
@@ -161,7 +160,7 @@ int ChannelConfig::convertToChannelNumberFromChannelName(
}
}
log<level::ERR>("Invalid channel name.",
- entry("Channel:%s", chName.c_str()));
+ entry("CHANNEL=%s", chName.c_str()));
throw std::invalid_argument("Invalid channel name");
return -1;
@@ -169,15 +168,15 @@ int ChannelConfig::convertToChannelNumberFromChannelName(
std::string ChannelConfig::getChannelNameFromPath(const std::string& path)
{
- std::size_t pos = path.find(networkIntfObjectBasePath);
- if (pos == std::string::npos)
+
+ constexpr size_t length = strlen(networkIntfObjectBasePath);
+ if (((length + 1) >= path.size()) ||
+ path.compare(0, length, networkIntfObjectBasePath))
{
- log<level::ERR>("Invalid interface path.",
- entry("PATH:%s", path.c_str()));
- throw std::invalid_argument("Invalid interface path");
+ log<level::ERR>("Invalid object path.", entry("PATH=%s", path.c_str()));
+ throw std::invalid_argument("Invalid object path");
}
- std::string chName =
- path.substr(pos + strlen(networkIntfObjectBasePath) + 1);
+ std::string chName(path, length + 1);
return chName;
}
@@ -192,7 +191,7 @@ void ChannelConfig::processChAccessPropChange(
}
catch (const std::invalid_argument& e)
{
- log<level::ERR>("Exception: ", entry("MSG: %s", e.what()));
+ log<level::ERR>("Exception: ", entry("MSG=%s", e.what()));
return;
}
@@ -204,7 +203,7 @@ void ChannelConfig::processChAccessPropChange(
if (prop.first == privilegePropertyString)
{
propName = privilegePropertyString;
- intfPrivStr = variant_ns::get<std::string>(prop.second);
+ intfPrivStr = std::get<std::string>(prop.second);
break;
}
}
@@ -218,7 +217,7 @@ void ChannelConfig::processChAccessPropChange(
if (intfPrivStr.empty())
{
log<level::ERR>("Invalid privilege string.",
- entry("INTF:%s", chName.c_str()));
+ entry("INTF=%s", chName.c_str()));
return;
}
@@ -231,7 +230,7 @@ void ChannelConfig::processChAccessPropChange(
}
catch (const std::invalid_argument& e)
{
- log<level::ERR>("Exception: ", entry("MSG: %s", e.what()));
+ log<level::ERR>("Exception: ", entry("MSG=%s", e.what()));
return;
}
@@ -614,7 +613,7 @@ ipmi_ret_t ChannelConfig::setChannelAccessPersistData(
{
log<level::DEBUG>(
"Network interface does not exist",
- entry("INTERFACE:%s", channelData[chNum].chName.c_str()));
+ entry("INTERFACE=%s", channelData[chNum].chName.c_str()));
return IPMI_CC_UNSPECIFIED_ERROR;
}
}
@@ -787,45 +786,13 @@ EChannelProtocolType
return static_cast<EChannelProtocolType>(it->second);
}
-uint8_t ChannelConfig::convertToChannelIndexNumber(const uint8_t chNum)
-{
-
- // TODO: There is limitation in current design. we cannot detect exact
- // LAN interface(eth0 or eth1) so Implementation may be updated
- // when there is any design update to figure out all the interfaces
- // independently based on the message.
-
- static uint8_t curChannel = 0xFF;
-
- if (curChannel == 0xFF)
- {
- auto it = interfaceMap.find(getInterfaceIndex());
- if (it == interfaceMap.end())
- {
- log<level::ERR>("Invalid Interface type ",
- entry("InterfaceIndex: %d", getInterfaceIndex()));
- throw std::invalid_argument("Invalid interface type.");
- }
-
- for (auto& channel : channelData)
- {
- std::string& interfaceName = it->second;
- if (channel.chName == interfaceName)
- {
- curChannel = channel.chID;
- break;
- }
- }
- }
- return ((chNum == currentChNum) ? curChannel : chNum);
-}
-
Json ChannelConfig::readJsonFile(const std::string& configFile)
{
std::ifstream jsonFile(configFile);
if (!jsonFile.good())
{
- log<level::ERR>("JSON file not found");
+ log<level::INFO>("JSON file not found",
+ entry("FILE_NAME=%s", configFile.c_str()));
return nullptr;
}
@@ -837,7 +804,7 @@ Json ChannelConfig::readJsonFile(const std::string& configFile)
catch (Json::parse_error& e)
{
log<level::DEBUG>("Corrupted channel config.",
- entry("MSG: %s", e.what()));
+ entry("MSG=%s", e.what()));
throw std::runtime_error("Corrupted channel config file");
}
@@ -847,17 +814,33 @@ Json ChannelConfig::readJsonFile(const std::string& configFile)
int ChannelConfig::writeJsonFile(const std::string& configFile,
const Json& jsonData)
{
- std::ofstream jsonFile(configFile);
- if (!jsonFile.good())
+ const std::string tmpFile = configFile + "_tmp";
+ int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0)
+ {
+ log<level::ERR>("Error in creating json file",
+ entry("FILE_NAME = %s", tmpFile.c_str()));
+ return -EIO;
+ }
+ const auto& writeData = jsonData.dump();
+ if (write(fd, writeData.c_str(), writeData.size()) !=
+ static_cast<ssize_t>(writeData.size()))
{
- log<level::ERR>("JSON file not found");
+ close(fd);
+ log<level::ERR>("Error in writing configuration file",
+ entry("FILE_NAME = %s", tmpFile.c_str()));
return -EIO;
}
+ close(fd);
- // Write JSON to file
- jsonFile << jsonData;
+ if (std::rename(tmpFile.c_str(), configFile.c_str()) != 0)
+ {
+ log<level::ERR>("Error in renaming temporary data file",
+ entry("FILE_NAME = %s", tmpFile.c_str()));
+ return -EIO;
+ }
- jsonFile.flush();
return 0;
}
@@ -900,7 +883,7 @@ int ChannelConfig::loadChannelConfig()
{
log<level::WARNING>(
"Channel not configured so loading default.",
- entry("CHANNEL_NUM:%d", chNum));
+ entry("CHANNEL_NUM=%d", chNum));
// If user didn't want to configure specific channel (say
// reserved channel), then load that index with default values.
setDefaultChannelConfig(chNum, defaultChannelName);
@@ -938,12 +921,12 @@ int ChannelConfig::loadChannelConfig()
catch (const Json::exception& e)
{
log<level::DEBUG>("Json Exception caught.",
- entry("MSG:%s", e.what()));
+ entry("MSG=%s", e.what()));
return -EBADMSG;
}
catch (const std::invalid_argument& e)
{
- log<level::ERR>("Corrupted config.", entry("MSG:%s", e.what()));
+ log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
return -EBADMSG;
}
}
@@ -997,7 +980,7 @@ int ChannelConfig::readChannelVolatileData()
{
log<level::ERR>(
"Invalid/corrupted volatile channel access file",
- entry("FILE: %s", channelVolatileDataFilename));
+ entry("FILE=%s", channelVolatileDataFilename));
throw std::runtime_error(
"Corrupted volatile channel access file");
}
@@ -1005,12 +988,12 @@ int ChannelConfig::readChannelVolatileData()
}
catch (const Json::exception& e)
{
- log<level::DEBUG>("Json Exception caught.", entry("MSG:%s", e.what()));
+ log<level::DEBUG>("Json Exception caught.", entry("MSG=%s", e.what()));
throw std::runtime_error("Corrupted volatile channel access file");
}
catch (const std::invalid_argument& e)
{
- log<level::ERR>("Corrupted config.", entry("MSG:%s", e.what()));
+ log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
throw std::runtime_error("Corrupted volatile channel access file");
}
@@ -1065,19 +1048,19 @@ int ChannelConfig::readChannelPersistData()
else
{
log<level::ERR>("Invalid/corrupted nv channel access file",
- entry("FILE:%s", channelNvDataFilename));
+ entry("FILE=%s", channelNvDataFilename));
throw std::runtime_error("Corrupted nv channel access file");
}
}
}
catch (const Json::exception& e)
{
- log<level::DEBUG>("Json Exception caught.", entry("MSG:%s", e.what()));
+ log<level::DEBUG>("Json Exception caught.", entry("MSG=%s", e.what()));
throw std::runtime_error("Corrupted nv channel access file");
}
catch (const std::invalid_argument& e)
{
- log<level::ERR>("Corrupted config.", entry("MSG: %s", e.what()));
+ log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
throw std::runtime_error("Corrupted nv channel access file");
}
@@ -1120,7 +1103,7 @@ int ChannelConfig::writeChannelVolatileData()
}
catch (const std::invalid_argument& e)
{
- log<level::ERR>("Corrupted config.", entry("MSG: %s", e.what()));
+ log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
return -EINVAL;
}
@@ -1171,7 +1154,7 @@ int ChannelConfig::writeChannelPersistData()
}
catch (const std::invalid_argument& e)
{
- log<level::ERR>("Corrupted config.", entry("MSG: %s", e.what()));
+ log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
return -EINVAL;
}
@@ -1245,10 +1228,10 @@ int ChannelConfig::setDbusProperty(const std::string& service,
catch (const sdbusplus::exception::SdBusError& e)
{
log<level::DEBUG>("set-property failed",
- entry("SERVICE:%s", service.c_str()),
- entry("OBJPATH:%s", objPath.c_str()),
- entry("INTERFACE:%s", interface.c_str()),
- entry("PROP:%s", property.c_str()));
+ entry("SERVICE=%s", service.c_str()),
+ entry("OBJPATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()),
+ entry("PROP=%s", property.c_str()));
return -EIO;
}
@@ -1275,10 +1258,10 @@ int ChannelConfig::getDbusProperty(const std::string& service,
catch (const sdbusplus::exception::SdBusError& e)
{
log<level::DEBUG>("get-property failed",
- entry("SERVICE:%s", service.c_str()),
- entry("OBJPATH:%s", objPath.c_str()),
- entry("INTERFACE:%s", interface.c_str()),
- entry("PROP:%s", property.c_str()));
+ entry("SERVICE=%s", service.c_str()),
+ entry("OBJPATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()),
+ entry("PROP=%s", property.c_str()));
return -EIO;
}
return 0;
@@ -1305,13 +1288,13 @@ int ChannelConfig::syncNetworkChannelConfig()
privilegePropertyString, variant))
{
log<level::DEBUG>("Network interface does not exist",
- entry("INTERFACE:%s",
+ entry("INTERFACE=%s",
channelData[chNum].chName.c_str()));
continue;
}
- intfPrivStr = variant_ns::get<std::string>(variant);
+ intfPrivStr = std::get<std::string>(variant);
}
- catch (const variant_ns::bad_variant_access& e)
+ catch (const std::bad_variant_access& e)
{
log<level::DEBUG>(
"exception: Network interface does not exist");
@@ -1358,6 +1341,9 @@ int ChannelConfig::syncNetworkChannelConfig()
void ChannelConfig::initChannelPersistData()
{
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ channelLock{*channelMutex};
+
/* Always read the channel config */
if (loadChannelConfig() != 0)
{
diff --git a/user_channel/channel_mgmt.hpp b/user_channel/channel_mgmt.hpp
index 35bb494..80bd6d9 100644
--- a/user_channel/channel_mgmt.hpp
+++ b/user_channel/channel_mgmt.hpp
@@ -23,14 +23,14 @@
#include <ctime>
#include <nlohmann/json.hpp>
#include <sdbusplus/bus.hpp>
+#include <variant>
namespace ipmi
{
using Json = nlohmann::json;
-using DbusVariant =
- sdbusplus::message::variant<std::vector<std::string>, std::string, bool>;
+using DbusVariant = std::variant<std::vector<std::string>, std::string, bool>;
using DbusChObjProperties = std::vector<std::pair<std::string, DbusVariant>>;
@@ -105,6 +105,18 @@ class ChannelConfig
*/
std::string getChannelName(const uint8_t chNum);
+ /** @brief function to get channel number from channel name
+ *
+ * @param[in] chName - channel name
+ *
+ * @return network channel interface number
+ */
+
+ uint8_t getChannelByName(const std::string& chName)
+ {
+ return convertToChannelNumberFromChannelName(chName);
+ }
+
/** @brief determines supported session type of a channel
*
* @param[in] chNum - channel number
@@ -212,14 +224,6 @@ class ChannelConfig
*/
CommandPrivilege convertToPrivLimitIndex(const std::string& value);
- /** @brief function to convert channel number to channel index
- *
- * @param[in] chNum - channel number
- *
- * @return channel index
- */
- uint8_t convertToChannelIndexNumber(const uint8_t chNum);
-
/** @brief function to write persistent channel configuration to config file
*
* @return 0 for success, -errno for failure.
diff --git a/user_channel/channelcommands.cpp b/user_channel/channelcommands.cpp
index d1b275e..adf19d5 100644
--- a/user_channel/channelcommands.cpp
+++ b/user_channel/channelcommands.cpp
@@ -14,11 +14,10 @@
// limitations under the License.
*/
-#include "channelcommands.hpp"
-
#include "apphandler.hpp"
#include "channel_layer.hpp"
+#include <ipmid/api.hpp>
#include <phosphor-logging/log.hpp>
#include <regex>
@@ -27,241 +26,99 @@ using namespace phosphor::logging;
namespace ipmi
{
-/** @struct SetChannelAccessReq
- *
- * Structure for set channel access request command (refer spec sec 22.22)
- */
-struct SetChannelAccessReq
-{
-#if BYTE_ORDER == LITTLE_ENDIAN
- uint8_t chNum : 4;
- uint8_t reserved_1 : 4;
- uint8_t accessMode : 3;
- uint8_t usrAuthDisabled : 1;
- uint8_t msgAuthDisabled : 1;
- uint8_t alertDisabled : 1;
- uint8_t accessSetMode : 2;
- uint8_t privLimit : 4;
- uint8_t reserved_2 : 2;
- uint8_t privSetMode : 2;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
- uint8_t reserved_1 : 4;
- uint8_t chNum : 4;
- uint8_t accessSetMode : 2;
- uint8_t alertDisabled : 1;
- uint8_t msgAuthDisabled : 1;
- uint8_t usrAuthDisabled : 1;
- uint8_t accessMode : 3;
- uint8_t privSetMode : 2;
- uint8_t reserved_2 : 2;
- uint8_t privLimit : 4;
-#endif
-
-} __attribute__((packed));
-
-/** @struct GetChannelAccessReq
- *
- * Structure for get channel access request command (refer spec sec 22.23)
- */
-struct GetChannelAccessReq
-{
-#if BYTE_ORDER == LITTLE_ENDIAN
- uint8_t chNum : 4;
- uint8_t reserved_1 : 4;
- uint8_t reserved_2 : 6;
- uint8_t accessSetMode : 2;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
- uint8_t reserved_1 : 4;
- uint8_t chNum : 4;
- uint8_t accessSetMode : 2;
- uint8_t reserved_2 : 6;
-#endif
-} __attribute__((packed));
-
-/** @struct GetChannelAccessResp
+static constexpr const uint8_t ccActionNotSupportedForChannel = 0x82;
+
+/** @brief implements the set channel access command
+ * @ param ctx - context pointer
+ * @ param channel - channel number
+ * @ param reserved - skip 4 bits
+ * @ param accessMode - access mode for IPMI messaging
+ * @ param usrAuth - user level authentication (enable/disable)
+ * @ param msgAuth - per message authentication (enable/disable)
+ * @ param alertDisabled - PEF alerting (enable/disable)
+ * @ param chanAccess - channel access
+ * @ param channelPrivLimit - channel privilege limit
+ * @ param reserved - skip 3 bits
+ * @ param channelPrivMode - channel priviledge mode
*
- * Structure for get channel access response command (refer spec sec 22.23)
- */
-struct GetChannelAccessResp
+ * @ returns IPMI completion code
+ **/
+RspType<> ipmiSetChannelAccess(Context::ptr ctx, uint4_t channel,
+ uint4_t reserved1, uint3_t accessMode,
+ bool usrAuth, bool msgAuth, bool alertDisabled,
+ uint2_t chanAccess, uint4_t channelPrivLimit,
+ uint2_t reserved2, uint2_t channelPrivMode)
{
-#if BYTE_ORDER == LITTLE_ENDIAN
- uint8_t accessMode : 3;
- uint8_t usrAuthDisabled : 1;
- uint8_t msgAuthDisabled : 1;
- uint8_t alertDisabled : 1;
- uint8_t reserved_1 : 2;
- uint8_t privLimit : 4;
- uint8_t reserved_2 : 4;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
- uint8_t reserved_1 : 2;
- uint8_t alertDisabled : 1;
- uint8_t msgAuthDisabled : 1;
- uint8_t usrAuthDisabled : 1;
- uint8_t accessMode : 3;
- uint8_t reserved_2 : 4;
- uint8_t privLimit : 4;
-#endif
-} __attribute__((packed));
-
-/** @struct GetChannelInfoReq
- *
- * Structure for get channel info request command (refer spec sec 22.24)
- */
-struct GetChannelInfoReq
-{
-#if BYTE_ORDER == LITTLE_ENDIAN
- uint8_t chNum : 4;
- uint8_t reserved_1 : 4;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
- uint8_t reserved_1 : 4;
- uint8_t chNum : 4;
-#endif
-} __attribute__((packed));
-
-/** @struct GetChannelInfoResp
- *
- * Structure for get channel info response command (refer spec sec 22.24)
- */
-struct GetChannelInfoResp
-{
-#if BYTE_ORDER == LITTLE_ENDIAN
- uint8_t chNum : 4;
- uint8_t reserved_1 : 4;
- uint8_t mediumType : 7;
- uint8_t reserved_2 : 1;
- uint8_t msgProtType : 5;
- uint8_t reserved_3 : 3;
- uint8_t actSessCount : 6;
- uint8_t sessType : 2;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
- uint8_t reserved_1 : 4;
- uint8_t chNum : 4;
- uint8_t reserved_2 : 1;
- uint8_t mediumType : 7;
- uint8_t reserved_3 : 3;
- uint8_t msgProtType : 5;
- uint8_t sessType : 2;
- uint8_t actSessCount : 6;
-#endif
- uint8_t vendorId[3];
- uint8_t auxChInfo[2];
-} __attribute__((packed));
-
-/** @struct GetChannelPayloadSupportReq
- *
- * Structure for get channel payload support command request (refer spec
- * sec 24.8)
- */
-struct GetChannelPayloadSupportReq
-{
-#if BYTE_ORDER == LITTLE_ENDIAN
- uint8_t chNum : 4;
- uint8_t reserved : 4;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
- uint8_t reserved : 4;
- uint8_t chNum : 4;
-#endif
-} __attribute__((packed));
-
-/** @struct GetChannelPayloadSupportResp
- *
- * Structure for get channel payload support command response (refer spec
- * sec 24.8)
- */
-struct GetChannelPayloadSupportResp
-{
- uint8_t stdPayloadType[2];
- uint8_t sessSetupPayloadType[2];
- uint8_t OEMPayloadType[2];
- uint8_t reserved[2];
-} __attribute__((packed));
-
-ipmi_ret_t ipmiSetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
-{
- const SetChannelAccessReq* req = static_cast<SetChannelAccessReq*>(request);
- size_t reqLength = *data_len;
-
- *data_len = 0;
+ const uint8_t chNum =
+ convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
- if (reqLength != sizeof(*req))
- {
- log<level::DEBUG>("Set channel access - Invalid Length");
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- uint8_t chNum = convertCurrentChannelNum(req->chNum);
- if (!isValidChannel(chNum) || req->reserved_1 != 0 || req->reserved_2 != 0)
+ if (!isValidChannel(chNum) || reserved1 != 0 || reserved2 != 0)
{
log<level::DEBUG>("Set channel access - Invalid field in request");
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return responseInvalidFieldRequest();
}
- if (EChannelSessSupported::none == getChannelSessionSupport(chNum))
+ if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
{
log<level::DEBUG>("Set channel access - No support on channel");
- return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL;
+ return response(ccActionNotSupportedForChannel);
}
ChannelAccess chActData;
ChannelAccess chNVData;
uint8_t setActFlag = 0;
uint8_t setNVFlag = 0;
- ipmi_ret_t compCode = IPMI_CC_OK;
+ Cc compCode;
- switch (req->accessSetMode)
+ // cannot static cast directly from uint2_t to enum; must go via int
+ uint8_t channelAccessAction = static_cast<uint8_t>(chanAccess);
+ switch (static_cast<EChannelActionType>(channelAccessAction))
{
case doNotSet:
- // Do nothing
break;
case nvData:
- chNVData.accessMode = req->accessMode;
- chNVData.userAuthDisabled = req->usrAuthDisabled;
- chNVData.perMsgAuthDisabled = req->msgAuthDisabled;
- chNVData.alertingDisabled = req->alertDisabled;
+ chNVData.accessMode = static_cast<uint8_t>(accessMode);
+ chNVData.userAuthDisabled = usrAuth;
+ chNVData.perMsgAuthDisabled = msgAuth;
+ chNVData.alertingDisabled = alertDisabled;
setNVFlag |= (setAccessMode | setUserAuthEnabled |
setMsgAuthEnabled | setAlertingEnabled);
break;
+
case activeData:
- chActData.accessMode = req->accessMode;
- chActData.userAuthDisabled = req->usrAuthDisabled;
- chActData.perMsgAuthDisabled = req->msgAuthDisabled;
- chActData.alertingDisabled = req->alertDisabled;
+ chActData.accessMode = static_cast<uint8_t>(accessMode);
+ chActData.userAuthDisabled = usrAuth;
+ chActData.perMsgAuthDisabled = msgAuth;
+ chActData.alertingDisabled = alertDisabled;
setActFlag |= (setAccessMode | setUserAuthEnabled |
setMsgAuthEnabled | setAlertingEnabled);
break;
+
case reserved:
default:
log<level::DEBUG>("Set channel access - Invalid access set mode");
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return responseInvalidFieldRequest();
}
- switch (req->privSetMode)
+ // cannot static cast directly from uint2_t to enum; must go via int
+ uint8_t channelPrivAction = static_cast<uint8_t>(channelPrivMode);
+ switch (static_cast<EChannelActionType>(channelPrivAction))
{
case doNotSet:
- // Do nothing
break;
case nvData:
- chNVData.privLimit = req->privLimit;
+ chNVData.privLimit = static_cast<uint8_t>(channelPrivLimit);
setNVFlag |= setPrivLimit;
break;
case activeData:
- chActData.privLimit = req->privLimit;
+ chActData.privLimit = static_cast<uint8_t>(channelPrivLimit);
+
setActFlag |= setPrivLimit;
break;
case reserved:
default:
log<level::DEBUG>("Set channel access - Invalid access priv mode");
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return responseInvalidFieldRequest();
}
if (setNVFlag != 0)
@@ -270,7 +127,7 @@ ipmi_ret_t ipmiSetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
if (compCode != IPMI_CC_OK)
{
log<level::DEBUG>("Set channel access - Failed to set access data");
- return compCode;
+ return response(compCode);
}
}
@@ -280,219 +137,250 @@ ipmi_ret_t ipmiSetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
if (compCode != IPMI_CC_OK)
{
log<level::DEBUG>("Set channel access - Failed to set access data");
- return compCode;
+ return response(compCode);
}
}
- return IPMI_CC_OK;
+ return responseSuccess();
}
-ipmi_ret_t ipmiGetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+/** @brief implements the get channel access command
+ * @ param ctx - context pointer
+ * @ param channel - channel number
+ * @ param reserved1 - skip 4 bits
+ * @ param reserved2 - skip 6 bits
+ * @ param accessMode - get access mode
+ *
+ * @returns ipmi completion code plus response data
+ * - accessMode - get access mode
+ * - usrAuthDisabled - user level authentication status
+ * - msgAuthDisabled - message level authentication status
+ * - alertDisabled - alerting status
+ * - reserved - skip 2 bits
+ * - privLimit - channel privilege limit
+ * - reserved - skip 4 bits
+ * */
+ipmi ::RspType<uint3_t, // access mode,
+ bool, // user authentication status,
+ bool, // message authentication status,
+ bool, // alerting status,
+ uint2_t, // reserved,
+
+ uint4_t, // channel privilege,
+ uint4_t // reserved
+ >
+ ipmiGetChannelAccess(Context::ptr ctx, uint4_t channel, uint4_t reserved1,
+ uint6_t reserved2, uint2_t accessSetMode)
{
- const GetChannelAccessReq* req = static_cast<GetChannelAccessReq*>(request);
- size_t reqLength = *data_len;
-
- *data_len = 0;
-
- if (reqLength != sizeof(*req))
- {
- log<level::DEBUG>("Get channel access - Invalid Length");
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
+ const uint8_t chNum =
+ convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
- uint8_t chNum = convertCurrentChannelNum(req->chNum);
- if (!isValidChannel(chNum) || req->reserved_1 != 0 || req->reserved_2 != 0)
+ if (!isValidChannel(chNum) || reserved1 != 0 || reserved2 != 0)
{
log<level::DEBUG>("Get channel access - Invalid field in request");
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return responseInvalidFieldRequest();
}
- if ((req->accessSetMode == doNotSet) || (req->accessSetMode == reserved))
+ if ((accessSetMode == doNotSet) || (accessSetMode == reserved))
{
log<level::DEBUG>("Get channel access - Invalid Access mode");
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return responseInvalidFieldRequest();
}
- if (EChannelSessSupported::none == getChannelSessionSupport(chNum))
+ if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
{
log<level::DEBUG>("Get channel access - No support on channel");
- return IPMI_CC_ACTION_NOT_SUPPORTED_FOR_CHANNEL;
+ return response(ccActionNotSupportedForChannel);
}
- GetChannelAccessResp* resp = static_cast<GetChannelAccessResp*>(response);
-
- std::fill(reinterpret_cast<uint8_t*>(resp),
- reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
-
ChannelAccess chAccess;
- ipmi_ret_t compCode = IPMI_CC_OK;
- if (req->accessSetMode == nvData)
+ Cc compCode;
+
+ if (accessSetMode == nvData)
{
compCode = getChannelAccessPersistData(chNum, chAccess);
}
- else if (req->accessSetMode == activeData)
+ else if (accessSetMode == activeData)
{
compCode = getChannelAccessData(chNum, chAccess);
}
if (compCode != IPMI_CC_OK)
{
- return compCode;
+ return response(compCode);
}
- resp->accessMode = chAccess.accessMode;
- resp->usrAuthDisabled = chAccess.userAuthDisabled;
- resp->msgAuthDisabled = chAccess.perMsgAuthDisabled;
- resp->alertDisabled = chAccess.alertingDisabled;
- resp->privLimit = chAccess.privLimit;
+ constexpr uint2_t reservedOut1 = 0;
+ constexpr uint4_t reservedOut2 = 0;
- *data_len = sizeof(*resp);
- return IPMI_CC_OK;
+ return responseSuccess(
+ static_cast<uint3_t>(chAccess.accessMode), chAccess.userAuthDisabled,
+ chAccess.perMsgAuthDisabled, chAccess.alertingDisabled, reservedOut1,
+ static_cast<uint4_t>(chAccess.privLimit), reservedOut2);
}
-ipmi_ret_t ipmiGetChannelInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request, ipmi_response_t response,
- ipmi_data_len_t data_len, ipmi_context_t context)
+/** @brief implements the get channel info command
+ * @ param ctx - context pointer
+ * @ param channel - channel number
+ * @ param reserved - skip 4 bits
+ *
+ * @returns ipmi completion code plus response data
+ * - chNum - the channel number for this request
+ * - mediumType - see Table 6-3, Channel Medium Type Numbers
+ * - protocolType - Table 6-2, Channel Protocol Type Numbers
+ * - activeSessionCount - number of active sessions
+ * - sessionType - channel support for sessions
+ * - vendorId - vendor for this channel protocol (IPMI - 7154)
+ * - auxChInfo - auxiliary info for channel
+ * */
+RspType<uint4_t, // chNum
+ uint4_t, // reserved
+ uint7_t, // mediumType
+ bool, // reserved
+ uint5_t, // protocolType
+ uint3_t, // reserved
+ uint6_t, // activeSessionCount
+ uint2_t, // sessionType
+ uint24_t, // Vendor IANA
+ uint16_t // aux info
+ >
+ ipmiGetChannelInfo(Context::ptr ctx, uint4_t channel, uint4_t reserved)
{
- const GetChannelInfoReq* req = static_cast<GetChannelInfoReq*>(request);
- size_t reqLength = *data_len;
-
- *data_len = 0;
-
- if (reqLength != sizeof(*req))
- {
- log<level::DEBUG>("Get channel info - Invalid Length");
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
-
- uint8_t chNum = convertCurrentChannelNum(req->chNum);
- if (!isValidChannel(chNum) || req->reserved_1 != 0)
+ uint8_t chNum =
+ convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
+ if (!isValidChannel(chNum) || reserved)
{
- log<level::DEBUG>("Get channel info - Invalid field in request");
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
-
- // Check the existance of device for session-less channels.
- if ((EChannelSessSupported::none != getChannelSessionSupport(chNum)) &&
- (!(doesDeviceExist(chNum))))
- {
- log<level::DEBUG>("Get channel info - Device not exist");
- return IPMI_CC_PARM_OUT_OF_RANGE;
+ log<level::DEBUG>("Get channel access - Invalid field in request");
+ return responseInvalidFieldRequest();
}
- GetChannelInfoResp* resp = static_cast<GetChannelInfoResp*>(response);
-
- std::fill(reinterpret_cast<uint8_t*>(resp),
- reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
-
ChannelInfo chInfo;
- ipmi_ret_t compCode = getChannelInfo(chNum, chInfo);
- if (compCode != IPMI_CC_OK)
+ Cc compCode = getChannelInfo(chNum, chInfo);
+ if (compCode != ccSuccess)
{
- return compCode;
+ log<level::ERR>("Failed to get channel info",
+ entry("CHANNEL=%x", chNum),
+ entry("ERRNO=%x", compCode));
+ return response(compCode);
}
- resp->chNum = chNum;
- resp->mediumType = chInfo.mediumType;
- resp->msgProtType = chInfo.protocolType;
- resp->actSessCount = getChannelActiveSessions(chNum);
- resp->sessType = chInfo.sessionSupported;
-
+ constexpr uint4_t reserved1 = 0;
+ constexpr bool reserved2 = false;
+ constexpr uint3_t reserved3 = 0;
+ uint8_t mediumType = chInfo.mediumType;
+ uint8_t protocolType = chInfo.protocolType;
+ uint2_t sessionType = chInfo.sessionSupported;
+ uint6_t activeSessionCount = getChannelActiveSessions(chNum);
// IPMI Spec: The IPMI Enterprise Number is: 7154 (decimal)
- resp->vendorId[0] = 0xF2;
- resp->vendorId[1] = 0x1B;
- resp->vendorId[2] = 0x00;
-
- // Auxiliary Channel info - byte 1:2
- // TODO: For System Interface(0xF) and OEM channel types, this needs
- // to be changed acoordingly.
- // All other channel types, its reverved
- resp->auxChInfo[0] = 0x00;
- resp->auxChInfo[1] = 0x00;
+ constexpr uint24_t vendorId = 7154;
+ constexpr uint16_t auxChInfo = 0;
- *data_len = sizeof(*resp);
-
- return IPMI_CC_OK;
+ return responseSuccess(chNum, reserved1, mediumType, reserved2,
+ protocolType, reserved3, activeSessionCount,
+ sessionType, vendorId, auxChInfo);
}
-ipmi_ret_t ipmiGetChannelPayloadSupport(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
+namespace
{
- const auto req = static_cast<GetChannelPayloadSupportReq*>(request);
- size_t reqLength = *data_len;
-
- *data_len = 0;
-
- if (reqLength != sizeof(*req))
- {
- log<level::DEBUG>("Get channel payload - Invalid Length");
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
+constexpr uint16_t standardPayloadBit(PayloadType p)
+{
+ return (1 << static_cast<size_t>(p));
+}
- uint8_t chNum = convertCurrentChannelNum(req->chNum);
- if (!isValidChannel(chNum) || req->reserved != 0)
- {
- log<level::DEBUG>("Get channel payload - Invalid field in request");
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
+constexpr uint16_t sessionPayloadBit(PayloadType p)
+{
+ constexpr size_t sessionShift =
+ static_cast<size_t>(PayloadType::OPEN_SESSION_REQUEST);
+ return ((1 << static_cast<size_t>(p)) >> sessionShift);
+}
+} // namespace
- // Not supported on sessionless channels.
- if (EChannelSessSupported::none == getChannelSessionSupport(chNum))
+/** @brief implements get channel payload support command
+ * @ param ctx - ipmi context pointer
+ * @ param chNum - channel number
+ * @ param reserved - skip 4 bits
+ *
+ * @ returns IPMI completion code plus response data
+ * - stdPayloadType - bitmask of supported standard payload types
+ * - sessSetupPayloadType - bitmask of supported session setup payload types
+ * - OEMPayloadType - bitmask of supported OEM payload types
+ * - reserved - 2 bytes of 0
+ **/
+RspType<uint16_t, // stdPayloadType
+ uint16_t, // sessSetupPayloadType
+ uint16_t, // OEMPayloadType
+ uint16_t // reserved
+ >
+ ipmiGetChannelPayloadSupport(Context::ptr ctx, uint4_t channel,
+ uint4_t reserved)
+{
+ uint8_t chNum =
+ convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
+ if (!isValidChannel(chNum) || reserved)
{
- log<level::DEBUG>("Get channel payload - Sessionless Channel");
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ log<level::DEBUG>("Get channel access - Invalid field in request");
+ return responseInvalidFieldRequest();
}
// Session support is available in active LAN channels.
- if ((EChannelSessSupported::none != getChannelSessionSupport(chNum)) &&
- (!(doesDeviceExist(chNum))))
+ if ((getChannelSessionSupport(chNum) == EChannelSessSupported::none) ||
+ !(doesDeviceExist(chNum)))
{
log<level::DEBUG>("Get channel payload - Device not exist");
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ return responseInvalidFieldRequest();
}
- auto resp = static_cast<GetChannelPayloadSupportResp*>(response);
+ constexpr uint16_t stdPayloadType = standardPayloadBit(PayloadType::IPMI) |
+ standardPayloadBit(PayloadType::SOL);
+ constexpr uint16_t sessSetupPayloadType =
+ sessionPayloadBit(PayloadType::OPEN_SESSION_REQUEST) |
+ sessionPayloadBit(PayloadType::OPEN_SESSION_RESPONSE) |
+ sessionPayloadBit(PayloadType::RAKP1) |
+ sessionPayloadBit(PayloadType::RAKP2) |
+ sessionPayloadBit(PayloadType::RAKP3) |
+ sessionPayloadBit(PayloadType::RAKP4);
+ constexpr uint16_t OEMPayloadType = 0;
+ constexpr uint16_t rspRsvd = 0;
+ return responseSuccess(stdPayloadType, sessSetupPayloadType, OEMPayloadType,
+ rspRsvd);
+}
- std::fill(reinterpret_cast<uint8_t*>(resp),
- reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
+/** @brief implements the get channel payload version command
+ * @param ctx - IPMI context pointer (for channel)
+ * @param chNum - channel number to get info about
+ * @param reserved - skip 4 bits
+ * @param payloadTypeNum - to get payload type info
- // TODO: Hard coding for now.
- // Mapping PayloadTypes to 'GetChannelPayloadSupportResp' fields:
- // --------------------------------------------------------------
- // Mask all except least 3 significant bits to get a value in the range of
- // 0-7. This value maps to the bit position of given payload type in 'resp'
- // fields.
+ * @returns IPMI completion code plus response data
+ * - formatVersion - BCD encoded format version info
+ */
- static constexpr uint8_t payloadByteMask = 0x07;
- static constexpr uint8_t stdPayloadTypeIPMI =
- 1 << (static_cast<uint8_t>(PayloadType::IPMI) & payloadByteMask);
- static constexpr uint8_t stdPayloadTypeSOL =
- 1 << (static_cast<uint8_t>(PayloadType::SOL) & payloadByteMask);
+RspType<uint8_t> // formatVersion
+ ipmiGetChannelPayloadVersion(Context::ptr ctx, uint4_t chNum,
+ uint4_t reserved, uint8_t payloadTypeNum)
+{
+ uint8_t channel =
+ convertCurrentChannelNum(static_cast<uint8_t>(chNum), ctx->channel);
- static constexpr uint8_t sessPayloadTypeOpenReq =
- 1 << (static_cast<uint8_t>(PayloadType::OPEN_SESSION_REQUEST) &
- payloadByteMask);
- static constexpr uint8_t sessPayloadTypeRAKP1 =
- 1 << (static_cast<uint8_t>(PayloadType::RAKP1) & payloadByteMask);
- static constexpr uint8_t sessPayloadTypeRAKP3 =
- 1 << (static_cast<uint8_t>(PayloadType::RAKP3) & payloadByteMask);
+ if (reserved || !isValidChannel(channel) ||
+ (getChannelSessionSupport(channel)) == EChannelSessSupported::none)
+ {
+ return responseInvalidFieldRequest();
+ }
+
+ if (!isValidPayloadType(static_cast<PayloadType>(payloadTypeNum)))
+ {
+ log<level::ERR>("Channel payload version - Payload type unavailable");
- resp->stdPayloadType[0] = stdPayloadTypeIPMI | stdPayloadTypeSOL;
- // RMCP+ Open Session request, RAKP Message1 and RAKP Message3.
- resp->sessSetupPayloadType[0] =
- sessPayloadTypeOpenReq | sessPayloadTypeRAKP1 | sessPayloadTypeRAKP3;
+ constexpr uint8_t payloadTypeNotSupported = 0x80;
+ return response(payloadTypeNotSupported);
+ }
- *data_len = sizeof(*resp);
+ // BCD encoded version representation - 1.0
+ constexpr uint8_t formatVersion = 0x10;
- return IPMI_CC_OK;
+ return responseSuccess(formatVersion);
}
void registerChannelFunctions() __attribute__((constructor));
@@ -500,19 +388,20 @@ void registerChannelFunctions()
{
ipmiChannelInit();
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHANNEL_ACCESS, NULL,
- ipmiSetChannelAccess, PRIVILEGE_ADMIN);
+ registerHandler(prioOpenBmcBase, netFnApp, app::cmdSetChannelAccess,
+ Privilege::Admin, ipmiSetChannelAccess);
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL,
- ipmiGetChannelAccess, PRIVILEGE_USER);
+ registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelAccess,
+ Privilege::User, ipmiGetChannelAccess);
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_INFO, NULL,
- ipmiGetChannelInfo, PRIVILEGE_USER);
+ registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelInfoCommand,
+ Privilege::User, ipmiGetChannelInfo);
- ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_PAYLOAD_SUPPORT,
- NULL, ipmiGetChannelPayloadSupport, PRIVILEGE_USER);
+ registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelPayloadSupport,
+ Privilege::User, ipmiGetChannelPayloadSupport);
- return;
+ registerHandler(prioOpenBmcBase, netFnApp, app::cmdGetChannelPayloadVersion,
+ Privilege::User, ipmiGetChannelPayloadVersion);
}
} // namespace ipmi
diff --git a/user_channel/channelcommands.hpp b/user_channel/channelcommands.hpp
deleted file mode 100644
index abcead6..0000000
--- a/user_channel/channelcommands.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-// Copyright (c) 2018 Intel Corporation
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-*/
-
-#pragma once
-#include <cstdint>
-
-namespace ipmi
-{
-
-/**
- * @enum IPMI commands for channel command NETFN:APP
- */
-enum ipmi_netfn_channel_cmds
-{
- IPMI_CMD_SET_CHANNEL_ACCESS = 0x40,
- IPMI_CMD_GET_CHANNEL_ACCESS = 0x41,
- IPMI_CMD_GET_CHANNEL_INFO = 0x42,
- IPMI_CMD_GET_CHANNEL_PAYLOAD_SUPPORT = 0x4E,
-};
-
-void registerChannelFunctions();
-} // namespace ipmi
diff --git a/user_channel/passwd_mgr.cpp b/user_channel/passwd_mgr.cpp
index 525b2b7..66bdef0 100644
--- a/user_channel/passwd_mgr.cpp
+++ b/user_channel/passwd_mgr.cpp
@@ -217,9 +217,10 @@ void PasswdMgr::initPasswordMap(void)
char* outPtr = reinterpret_cast<char*>(dataBuf.data());
char* nToken = NULL;
char* linePtr = strtok_r(outPtr, "\n", &nToken);
- size_t userEPos = 0, lineSize = 0;
+ size_t lineSize = 0;
while (linePtr != NULL)
{
+ size_t userEPos = 0;
std::string lineStr(linePtr);
if ((userEPos = lineStr.find(":")) != std::string::npos)
{
@@ -355,11 +356,12 @@ int PasswdMgr::updatePasswdSpecialFile(const std::string& userName,
if (inBytesLen != 0)
{
char* outPtr = reinterpret_cast<char*>(dataBuf.data());
- size_t userEPos = 0;
char* nToken = NULL;
char* linePtr = strtok_r(outPtr, "\n", &nToken);
while (linePtr != NULL)
{
+ size_t userEPos = 0;
+
std::string lineStr(linePtr);
if ((userEPos = lineStr.find(":")) != std::string::npos)
{
@@ -441,7 +443,6 @@ int PasswdMgr::updatePasswdSpecialFile(const std::string& userName,
log<level::DEBUG>("Error creating temp file");
return -EIO;
}
- fd = -1; // don't use fd anymore, as the File object owns it
// Set the file mode as of actual ipmi-pass file.
if (fchmod(fileno((temp)()), st.st_mode) < 0)
diff --git a/user_channel/user_layer.cpp b/user_channel/user_layer.cpp
index b241564..b309e86 100644
--- a/user_channel/user_layer.cpp
+++ b/user_channel/user_layer.cpp
@@ -88,6 +88,12 @@ ipmi_ret_t ipmiUserSetUserPassword(const uint8_t userId,
return getUserAccessObject().setUserPassword(userId, userPassword);
}
+ipmi_ret_t ipmiSetSpecialUserPassword(const std::string& userName,
+ const std::string& userPassword)
+{
+ return getUserAccessObject().setSpecialUserPassword(userName, userPassword);
+}
+
ipmi_ret_t ipmiUserGetAllCounts(uint8_t& maxChUsers, uint8_t& enabledUsers,
uint8_t& fixedUsers)
{
@@ -164,4 +170,57 @@ ipmi_ret_t ipmiUserSetPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
userId, chNum, userPrivAccess, otherPrivUpdates);
}
+bool ipmiUserPamAuthenticate(std::string_view userName,
+ std::string_view userPassword)
+{
+ return pamUserCheckAuthenticate(userName, userPassword);
+}
+
+ipmi_ret_t ipmiUserSetUserPayloadAccess(const uint8_t chNum,
+ const uint8_t operation,
+ const uint8_t userId,
+ const PayloadAccess& payloadAccess)
+{
+
+ if (!UserAccess::isValidChannel(chNum))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (!UserAccess::isValidUserId(userId))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ return getUserAccessObject().setUserPayloadAccess(chNum, operation, userId,
+ payloadAccess);
+}
+
+ipmi_ret_t ipmiUserGetUserPayloadAccess(const uint8_t chNum,
+ const uint8_t userId,
+ PayloadAccess& payloadAccess)
+{
+
+ if (!UserAccess::isValidChannel(chNum))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (!UserAccess::isValidUserId(userId))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ UserInfo* userInfo = getUserAccessObject().getUserInfo(userId);
+
+ payloadAccess.stdPayloadEnables1 =
+ userInfo->payloadAccess[chNum].stdPayloadEnables1;
+ payloadAccess.stdPayloadEnables2Reserved =
+ userInfo->payloadAccess[chNum].stdPayloadEnables2Reserved;
+ payloadAccess.oemPayloadEnables1 =
+ userInfo->payloadAccess[chNum].oemPayloadEnables1;
+ payloadAccess.oemPayloadEnables2Reserved =
+ userInfo->payloadAccess[chNum].oemPayloadEnables2Reserved;
+
+ return IPMI_CC_OK;
+}
+
} // namespace ipmi
diff --git a/user_channel/user_layer.hpp b/user_channel/user_layer.hpp
index 5f3567a..450d878 100644
--- a/user_channel/user_layer.hpp
+++ b/user_channel/user_layer.hpp
@@ -16,6 +16,7 @@
#pragma once
#include <ipmid/api.h>
+#include <bitset>
#include <string>
namespace ipmi
@@ -37,6 +38,7 @@ static constexpr uint8_t ipmiMaxUsers = 15;
static constexpr uint8_t ipmiMaxChannels = 16;
static constexpr uint8_t maxIpmi20PasswordSize = 20;
static constexpr uint8_t maxIpmi15PasswordSize = 16;
+static constexpr uint8_t payloadsPerByte = 8;
/** @struct PrivAccess
*
@@ -61,6 +63,19 @@ struct PrivAccess
#endif
} __attribute__((packed));
+/** @struct UserPayloadAccess
+ *
+ * Structure to denote payload access restrictions applicable for a
+ * given user and channel. (refer spec sec 24.6)
+ */
+struct PayloadAccess
+{
+ std::bitset<payloadsPerByte> stdPayloadEnables1;
+ std::bitset<payloadsPerByte> stdPayloadEnables2Reserved;
+ std::bitset<payloadsPerByte> oemPayloadEnables1;
+ std::bitset<payloadsPerByte> oemPayloadEnables2Reserved;
+};
+
/** @brief initializes user management
*
* @return IPMI_CC_OK for success, others for failure.
@@ -138,6 +153,16 @@ ipmi_ret_t ipmiUserSetUserName(const uint8_t userId, const char* userName);
ipmi_ret_t ipmiUserSetUserPassword(const uint8_t userId,
const char* userPassword);
+/** @brief set special user password (non-ipmi accounts)
+ *
+ * @param[in] userName - user name
+ * @param[in] userPassword - New Password
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiSetSpecialUserPassword(const std::string& userName,
+ const std::string& userPassword);
+
/** @brief get user name
*
* @param[in] userId - user id
@@ -200,4 +225,41 @@ ipmi_ret_t ipmiUserSetPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
const PrivAccess& privAccess,
const bool& otherPrivUpdate);
+/** @brief check for user pam authentication. This is to determine, whether user
+ * is already locked out for failed login attempt
+ *
+ * @param[in] username - username
+ * @param[in] password - password
+ *
+ * @return status
+ */
+bool ipmiUserPamAuthenticate(std::string_view userName,
+ std::string_view userPassword);
+
+/** @brief sets user payload access data
+ *
+ * @param[in] chNum - channel number
+ * @param[in] operation - ENABLE / DISABLE operation
+ * @param[in] userId - user id
+ * @param[in] payloadAccess - payload access data
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserSetUserPayloadAccess(const uint8_t chNum,
+ const uint8_t operation,
+ const uint8_t userId,
+ const PayloadAccess& payloadAccess);
+
+/** @brief provides user payload access data
+ *
+ * @param[in] chNum - channel number
+ * @param[in] userId - user id
+ * @param[out] payloadAccess - payload access data
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserGetUserPayloadAccess(const uint8_t chNum,
+ const uint8_t userId,
+ PayloadAccess& payloadAccess);
+
} // namespace ipmi
diff --git a/user_channel/user_mgmt.cpp b/user_channel/user_mgmt.cpp
index f0cd7ad..102f990 100644
--- a/user_channel/user_mgmt.cpp
+++ b/user_channel/user_mgmt.cpp
@@ -16,6 +16,7 @@
#include "user_mgmt.hpp"
#include "apphandler.hpp"
+#include "channel_layer.hpp"
#include <security/pam_appl.h>
#include <sys/stat.h>
@@ -31,6 +32,7 @@
#include <regex>
#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/server/object.hpp>
+#include <variant>
#include <xyz/openbmc_project/Common/error.hpp>
#include <xyz/openbmc_project/User/Common/error.hpp>
@@ -103,13 +105,10 @@ static std::array<std::string, (PRIVILEGE_OEM + 1)> ipmiPrivIndex = {
"priv-custom" // PRIVILEGE_OEM - 5
};
-namespace variant_ns = sdbusplus::message::variant_ns;
-
using namespace phosphor::logging;
using Json = nlohmann::json;
-using PrivAndGroupType =
- sdbusplus::message::variant<std::string, std::vector<std::string>>;
+using PrivAndGroupType = std::variant<std::string, std::vector<std::string>>;
using NoResource =
sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource;
@@ -198,12 +197,13 @@ UserAccess& getUserAccessObject()
int getUserNameFromPath(const std::string& path, std::string& userName)
{
- static size_t pos = strlen(userObjBasePath) + 1;
- if (path.find(userObjBasePath) == std::string::npos)
+ constexpr size_t length = strlen(userObjBasePath);
+ if (((length + 1) >= path.size()) ||
+ path.compare(0, length, userObjBasePath))
{
return -EINVAL;
}
- userName.assign(path, pos, path.size());
+ userName.assign(path, length + 1, path.size());
return 0;
}
@@ -308,7 +308,7 @@ void userUpdatedSignalHandler(UserAccess& usrAccess,
{
static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
std::string signal = msg.get_member();
- std::string userName, update, priv, newUserName;
+ std::string userName, priv, newUserName;
std::vector<std::string> groups;
bool enabled = false;
UserUpdateEvent userEvent = UserUpdateEvent::reservedEvent;
@@ -376,17 +376,17 @@ void userUpdatedSignalHandler(UserAccess& usrAccess,
std::string member = prop.first;
if (member == userPrivProperty)
{
- priv = variant_ns::get<std::string>(prop.second);
+ priv = std::get<std::string>(prop.second);
userEvent = UserUpdateEvent::userPrivUpdated;
}
else if (member == userGrpProperty)
{
- groups = variant_ns::get<std::vector<std::string>>(prop.second);
+ groups = std::get<std::vector<std::string>>(prop.second);
userEvent = UserUpdateEvent::userGrpUpdated;
}
else if (member == userEnabledProperty)
{
- enabled = variant_ns::get<bool>(prop.second);
+ enabled = std::get<bool>(prop.second);
userEvent = UserUpdateEvent::userStateUpdated;
}
// Process based on event type.
@@ -472,43 +472,8 @@ UserAccess::UserAccess() : bus(ipmid_get_sd_bus_connection())
userMutex = std::make_unique<boost::interprocess::named_recursive_mutex>(
boost::interprocess::open_or_create, ipmiUserMutex);
- initUserDataFile();
+ cacheUserDataFile();
getSystemPrivAndGroups();
- sigHndlrLock = boost::interprocess::file_lock(ipmiUserDataFile);
- // Register it for single object and single process either netipimd /
- // host-ipmid
- if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
- {
- log<level::DEBUG>("Registering signal handler");
- userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
- bus,
- sdbusplus::bus::match::rules::type::signal() +
- sdbusplus::bus::match::rules::interface(dBusObjManager) +
- sdbusplus::bus::match::rules::path(userMgrObjBasePath),
- [&](sdbusplus::message::message& msg) {
- userUpdatedSignalHandler(*this, msg);
- });
- userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
- bus,
- sdbusplus::bus::match::rules::type::signal() +
- sdbusplus::bus::match::rules::interface(userMgrInterface) +
- sdbusplus::bus::match::rules::path(userMgrObjBasePath),
- [&](sdbusplus::message::message& msg) {
- userUpdatedSignalHandler(*this, msg);
- });
- userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
- bus,
- sdbusplus::bus::match::rules::type::signal() +
- sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
- sdbusplus::bus::match::rules::interface(
- dBusPropertiesInterface) +
- sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
- sdbusplus::bus::match::rules::argN(0, usersInterface),
- [&](sdbusplus::message::message& msg) {
- userUpdatedSignalHandler(*this, msg);
- });
- signalHndlrObject = true;
- }
}
UserInfo* UserAccess::getUserInfo(const uint8_t userId)
@@ -683,33 +648,64 @@ static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
* @return status
*/
-bool pamUpdatePasswd(const char* username, const char* password)
+int pamUpdatePasswd(const char* username, const char* password)
{
const struct pam_conv localConversation = {pamFunctionConversation,
const_cast<char*>(password)};
pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
- if (pam_start("passwd", username, &localConversation, &localAuthHandle) !=
- PAM_SUCCESS)
+ int retval =
+ pam_start("passwd", username, &localConversation, &localAuthHandle);
+
+ if (retval != PAM_SUCCESS)
{
+ return retval;
+ }
+
+ retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
+ if (retval != PAM_SUCCESS)
+ {
+ pam_end(localAuthHandle, retval);
+ return retval;
+ }
+
+ return pam_end(localAuthHandle, PAM_SUCCESS);
+}
+
+bool pamUserCheckAuthenticate(std::string_view username,
+ std::string_view password)
+{
+ const struct pam_conv localConversation = {
+ pamFunctionConversation, const_cast<char*>(password.data())};
+
+ pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
+
+ if (pam_start("dropbear", username.data(), &localConversation,
+ &localAuthHandle) != PAM_SUCCESS)
+ {
+ log<level::ERR>("User Authentication Failure");
return false;
}
- int retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
+
+ int retval = pam_authenticate(localAuthHandle,
+ PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
if (retval != PAM_SUCCESS)
{
- if (retval == PAM_AUTHTOK_ERR)
- {
- log<level::DEBUG>("Authentication Failure");
- }
- else
- {
- log<level::DEBUG>("pam_chauthtok returned failure",
- entry("ERROR=%d", retval));
- }
+ log<level::DEBUG>("pam_authenticate returned failure",
+ entry("ERROR=%d", retval));
+
pam_end(localAuthHandle, retval);
return false;
}
+
+ if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
+ PAM_SUCCESS)
+ {
+ pam_end(localAuthHandle, PAM_SUCCESS);
+ return false;
+ }
+
if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
{
return false;
@@ -717,33 +713,51 @@ bool pamUpdatePasswd(const char* username, const char* password)
return true;
}
+ipmi_ret_t UserAccess::setSpecialUserPassword(const std::string& userName,
+ const std::string& userPassword)
+{
+ if (pamUpdatePasswd(userName.c_str(), userPassword.c_str()) != PAM_SUCCESS)
+ {
+ log<level::DEBUG>("Failed to update password");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ return IPMI_CC_OK;
+}
+
ipmi_ret_t UserAccess::setUserPassword(const uint8_t userId,
const char* userPassword)
{
std::string userName;
- if (ipmiUserGetUserName(userId, userName) != IPMI_CC_OK)
+ if (ipmiUserGetUserName(userId, userName) != ipmi::ccSuccess)
{
log<level::DEBUG>("User Name not found",
- entry("USER-ID:%d", (uint8_t)userId));
- return IPMI_CC_PARM_OUT_OF_RANGE;
+ entry("USER-ID=%d", (uint8_t)userId));
+ return ipmi::ccParmOutOfRange;
}
std::string passwd;
passwd.assign(reinterpret_cast<const char*>(userPassword), 0,
maxIpmi20PasswordSize);
- if (!std::regex_match(passwd.c_str(),
- std::regex("[a-zA-z_0-9][a-zA-Z_0-9,?:`!\"]*")))
- {
- log<level::DEBUG>("Invalid password fields",
- entry("USER-ID:%d", (uint8_t)userId));
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
- if (!pamUpdatePasswd(userName.c_str(), passwd.c_str()))
+
+ int retval = pamUpdatePasswd(userName.c_str(), passwd.c_str());
+
+ switch (retval)
{
- log<level::DEBUG>("Failed to update password",
- entry("USER-ID:%d", (uint8_t)userId));
- return IPMI_CC_UNSPECIFIED_ERROR;
+ case PAM_SUCCESS:
+ {
+ return ipmi::ccSuccess;
+ }
+ case PAM_AUTHTOK_ERR:
+ {
+ log<level::DEBUG>("Bad authentication token");
+ return ipmi::ccInvalidFieldRequest;
+ }
+ default:
+ {
+ log<level::DEBUG>("Failed to update password",
+ entry("USER-ID=%d", (uint8_t)userId));
+ return ipmi::ccUnspecifiedError;
+ }
}
- return IPMI_CC_OK;
}
ipmi_ret_t UserAccess::setUserEnabledState(const uint8_t userId,
@@ -783,6 +797,60 @@ ipmi_ret_t UserAccess::setUserEnabledState(const uint8_t userId,
return IPMI_CC_OK;
}
+ipmi_ret_t UserAccess::setUserPayloadAccess(const uint8_t chNum,
+ const uint8_t operation,
+ const uint8_t userId,
+ const PayloadAccess& payloadAccess)
+{
+ constexpr uint8_t enable = 0x0;
+ constexpr uint8_t disable = 0x1;
+
+ if (!isValidChannel(chNum))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (!isValidUserId(userId))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ if (operation != enable && operation != disable)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ // Check operation & payloadAccess if required.
+ boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+ userLock{*userMutex};
+ UserInfo* userInfo = getUserInfo(userId);
+
+ if (operation == enable)
+ {
+ userInfo->payloadAccess[chNum].stdPayloadEnables1 |=
+ payloadAccess.stdPayloadEnables1;
+
+ userInfo->payloadAccess[chNum].oemPayloadEnables1 |=
+ payloadAccess.oemPayloadEnables1;
+ }
+ else
+ {
+ userInfo->payloadAccess[chNum].stdPayloadEnables1 &=
+ ~(payloadAccess.stdPayloadEnables1);
+
+ userInfo->payloadAccess[chNum].oemPayloadEnables1 &=
+ ~(payloadAccess.oemPayloadEnables1);
+ }
+
+ try
+ {
+ writeUserData();
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Write user data failed");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ return IPMI_CC_OK;
+}
+
ipmi_ret_t UserAccess::setUserPrivilegeAccess(const uint8_t userId,
const uint8_t chNum,
const UserPrivAccess& privAccess,
@@ -878,6 +946,26 @@ ipmi_ret_t UserAccess::getUserName(const uint8_t userId, std::string& userName)
return IPMI_CC_OK;
}
+bool UserAccess::isIpmiInAvailableGroupList()
+{
+ if (std::find(availableGroups.begin(), availableGroups.end(),
+ ipmiGrpName) != availableGroups.end())
+ {
+ return true;
+ }
+ if (availableGroups.empty())
+ {
+ // available groups shouldn't be empty, re-query
+ getSystemPrivAndGroups();
+ if (std::find(availableGroups.begin(), availableGroups.end(),
+ ipmiGrpName) != availableGroups.end())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
ipmi_ret_t UserAccess::setUserName(const uint8_t userId,
const char* userNameInChar)
{
@@ -923,6 +1011,10 @@ ipmi_ret_t UserAccess::setUserName(const uint8_t userId,
{
try
{
+ if (!isIpmiInAvailableGroupList())
+ {
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
// Create new user
auto method = bus.new_method_call(
getUserServiceName().c_str(), userMgrObjBasePath,
@@ -990,6 +1082,98 @@ static constexpr const char* jsonAccCallbk = "access_callback";
static constexpr const char* jsonUserEnabled = "user_enabled";
static constexpr const char* jsonUserInSys = "user_in_system";
static constexpr const char* jsonFixedUser = "fixed_user_name";
+static constexpr const char* payloadEnabledStr = "payload_enabled";
+static constexpr const char* stdPayloadStr = "std_payload";
+static constexpr const char* oemPayloadStr = "OEM_payload";
+
+/** @brief to construct a JSON object from the given payload access details.
+ *
+ * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array. (input)
+ * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array. (input)
+ *
+ * @details Sample output JSON object format :
+ * "payload_enabled":{
+ * "OEM_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "OEM_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "OEM_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "OEM_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "OEM_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "OEM_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "OEM_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "OEM_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "std_payload0":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "std_payload1":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "std_payload2":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "std_payload3":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "std_payload4":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "std_payload5":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "std_payload6":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * "std_payload7":[false,...<repeat 'ipmiMaxChannels - 1' times>],
+ * }
+ */
+static const Json constructJsonPayloadEnables(
+ const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+ stdPayload,
+ const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+ oemPayload)
+{
+ Json jsonPayloadEnabled;
+
+ for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
+ {
+ std::ostringstream stdPayloadStream;
+ std::ostringstream oemPayloadStream;
+
+ stdPayloadStream << stdPayloadStr << payloadNum;
+ oemPayloadStream << oemPayloadStr << payloadNum;
+
+ jsonPayloadEnabled.push_back(Json::object_t::value_type(
+ stdPayloadStream.str(), stdPayload[payloadNum]));
+
+ jsonPayloadEnabled.push_back(Json::object_t::value_type(
+ oemPayloadStream.str(), oemPayload[payloadNum]));
+ }
+ return jsonPayloadEnabled;
+}
+
+void UserAccess::readPayloadAccessFromUserInfo(
+ const UserInfo& userInfo,
+ std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& stdPayload,
+ std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>& oemPayload)
+{
+ for (auto payloadNum = 0; payloadNum < payloadsPerByte; payloadNum++)
+ {
+ for (auto chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
+ {
+ stdPayload[payloadNum][chIndex] =
+ userInfo.payloadAccess[chIndex].stdPayloadEnables1[payloadNum];
+
+ oemPayload[payloadNum][chIndex] =
+ userInfo.payloadAccess[chIndex].oemPayloadEnables1[payloadNum];
+ }
+ }
+}
+
+void UserAccess::updatePayloadAccessInUserInfo(
+ const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+ stdPayload,
+ const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+ oemPayload,
+ UserInfo& userInfo)
+{
+ for (size_t chIndex = 0; chIndex < ipmiMaxChannels; ++chIndex)
+ {
+ // Ensure that reserved/unsupported payloads are marked to zero.
+ userInfo.payloadAccess[chIndex].stdPayloadEnables1.reset();
+ userInfo.payloadAccess[chIndex].oemPayloadEnables1.reset();
+ userInfo.payloadAccess[chIndex].stdPayloadEnables2Reserved.reset();
+ userInfo.payloadAccess[chIndex].oemPayloadEnables2Reserved.reset();
+ // Update SOL status as it is the only supported payload currently.
+ userInfo.payloadAccess[chIndex]
+ .stdPayloadEnables1[static_cast<uint8_t>(ipmi::PayloadType::SOL)] =
+ stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)][chIndex];
+ }
+}
void UserAccess::readUserData()
{
@@ -1013,6 +1197,7 @@ void UserAccess::readUserData()
throw std::runtime_error(
"Corrupted IPMI user data file - invalid user count");
}
+
// user index 0 is reserved, starts with 1
for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
{
@@ -1036,6 +1221,48 @@ void UserAccess::readUserData()
userInfo[jsonLinkAuthEnabled].get<std::vector<bool>>();
std::vector<bool> accessCallback =
userInfo[jsonAccCallbk].get<std::vector<bool>>();
+
+ // Payload Enables Processing.
+ std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
+ stdPayload = {};
+ std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
+ oemPayload = {};
+ try
+ {
+ const auto jsonPayloadEnabled = userInfo.at(payloadEnabledStr);
+ for (auto payloadNum = 0; payloadNum < payloadsPerByte;
+ payloadNum++)
+ {
+ std::ostringstream stdPayloadStream;
+ std::ostringstream oemPayloadStream;
+
+ stdPayloadStream << stdPayloadStr << payloadNum;
+ oemPayloadStream << oemPayloadStr << payloadNum;
+
+ stdPayload[payloadNum] =
+ jsonPayloadEnabled[stdPayloadStream.str()]
+ .get<std::array<bool, ipmiMaxChannels>>();
+ oemPayload[payloadNum] =
+ jsonPayloadEnabled[oemPayloadStream.str()]
+ .get<std::array<bool, ipmiMaxChannels>>();
+
+ if (stdPayload[payloadNum].size() != ipmiMaxChannels ||
+ oemPayload[payloadNum].size() != ipmiMaxChannels)
+ {
+ log<level::ERR>("Error in reading IPMI user data file - "
+ "payload properties corrupted");
+ throw std::runtime_error(
+ "Corrupted IPMI user data file - payload properties");
+ }
+ }
+ }
+ catch (Json::out_of_range& e)
+ {
+ // Key not found in 'userInfo'; possibly an old JSON file. Use
+ // default values for all payloads, and SOL payload default is true.
+ stdPayload[static_cast<uint8_t>(ipmi::PayloadType::SOL)].fill(true);
+ }
+
if (privilege.size() != ipmiMaxChannels ||
ipmiEnabled.size() != ipmiMaxChannels ||
linkAuthEnabled.size() != ipmiMaxChannels ||
@@ -1058,6 +1285,8 @@ void UserAccess::readUserData()
usersTbl.user[usrIndex].userPrivAccess[chIndex].accessCallback =
accessCallback[chIndex];
}
+ updatePayloadAccessInUserInfo(stdPayload, oemPayload,
+ usersTbl.user[usrIndex]);
usersTbl.user[usrIndex].userEnabled =
userInfo[jsonUserEnabled].get<bool>();
usersTbl.user[usrIndex].userInSystem =
@@ -1078,15 +1307,6 @@ void UserAccess::writeUserData()
boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
userLock{*userMutex};
- static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
- std::ofstream oUsrData(tmpFile, std::ios::out | std::ios::binary);
- if (!oUsrData.good())
- {
- log<level::ERR>("Error in creating temporary IPMI user data file");
- throw std::ios_base::failure(
- "Error in creating temporary IPMI user data file");
- }
-
Json jsonUsersTbl = Json::array();
// user index 0 is reserved, starts with 1
for (size_t usrIndex = 1; usrIndex <= ipmiMaxUsers; ++usrIndex)
@@ -1099,6 +1319,12 @@ void UserAccess::writeUserData()
std::vector<bool> ipmiEnabled(ipmiMaxChannels);
std::vector<bool> linkAuthEnabled(ipmiMaxChannels);
std::vector<bool> accessCallback(ipmiMaxChannels);
+
+ std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
+ stdPayload;
+ std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>
+ oemPayload;
+
for (size_t chIndex = 0; chIndex < ipmiMaxChannels; chIndex++)
{
privilege[chIndex] =
@@ -1118,12 +1344,35 @@ void UserAccess::writeUserData()
jsonUserInfo[jsonUserEnabled] = usersTbl.user[usrIndex].userEnabled;
jsonUserInfo[jsonUserInSys] = usersTbl.user[usrIndex].userInSystem;
jsonUserInfo[jsonFixedUser] = usersTbl.user[usrIndex].fixedUserName;
+
+ readPayloadAccessFromUserInfo(usersTbl.user[usrIndex], stdPayload,
+ oemPayload);
+ Json jsonPayloadEnabledInfo =
+ constructJsonPayloadEnables(stdPayload, oemPayload);
+ jsonUserInfo[payloadEnabledStr] = jsonPayloadEnabledInfo;
+
jsonUsersTbl.push_back(jsonUserInfo);
}
- oUsrData << jsonUsersTbl;
- oUsrData.flush();
- oUsrData.close();
+ static std::string tmpFile{std::string(ipmiUserDataFile) + "_tmp"};
+ int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0)
+ {
+ log<level::ERR>("Error in creating temporary IPMI user data file");
+ throw std::ios_base::failure(
+ "Error in creating temporary IPMI user data file");
+ }
+ const auto& writeStr = jsonUsersTbl.dump();
+ if (write(fd, writeStr.c_str(), writeStr.size()) !=
+ static_cast<ssize_t>(writeStr.size()))
+ {
+ close(fd);
+ log<level::ERR>("Error in writing temporary IPMI user data file");
+ throw std::ios_base::failure(
+ "Error in writing temporary IPMI user data file");
+ }
+ close(fd);
if (std::rename(tmpFile.c_str(), ipmiUserDataFile) != 0)
{
@@ -1252,13 +1501,11 @@ void UserAccess::getSystemPrivAndGroups()
auto key = t.first;
if (key == allPrivProperty)
{
- availablePrivileges =
- variant_ns::get<std::vector<std::string>>(t.second);
+ availablePrivileges = std::get<std::vector<std::string>>(t.second);
}
else if (key == allGrpProperty)
{
- availableGroups =
- variant_ns::get<std::vector<std::string>>(t.second);
+ availableGroups = std::get<std::vector<std::string>>(t.second);
}
}
// TODO: Implement Supported Privilege & Groups verification logic
@@ -1285,15 +1532,15 @@ void UserAccess::getUserProperties(const DbusUserObjProperties& properties,
std::string key = t.first;
if (key == userPrivProperty)
{
- usrPriv = variant_ns::get<std::string>(t.second);
+ usrPriv = std::get<std::string>(t.second);
}
else if (key == userGrpProperty)
{
- usrGrps = variant_ns::get<std::vector<std::string>>(t.second);
+ usrGrps = std::get<std::vector<std::string>>(t.second);
}
else if (key == userEnabledProperty)
{
- usrEnabled = variant_ns::get<bool>(t.second);
+ usrEnabled = std::get<bool>(t.second);
}
}
return;
@@ -1312,7 +1559,7 @@ int UserAccess::getUserObjProperties(const DbusUserObjValue& userObjs,
return -EIO;
}
-void UserAccess::initUserDataFile()
+void UserAccess::cacheUserDataFile()
{
boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
userLock{*userMutex};
@@ -1331,10 +1578,49 @@ void UserAccess::initUserDataFile()
{
usersTbl.user[userIndex].userPrivAccess[chIndex].privilege =
privNoAccess;
+ usersTbl.user[userIndex]
+ .payloadAccess[chIndex]
+ .stdPayloadEnables1[static_cast<uint8_t>(
+ ipmi::PayloadType::SOL)] = true;
}
}
writeUserData();
}
+ sigHndlrLock = boost::interprocess::file_lock(ipmiUserDataFile);
+ // Register it for single object and single process either netipimd /
+ // host-ipmid
+ if (userUpdatedSignal == nullptr && sigHndlrLock.try_lock())
+ {
+ log<level::DEBUG>("Registering signal handler");
+ userUpdatedSignal = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::interface(dBusObjManager) +
+ sdbusplus::bus::match::rules::path(userMgrObjBasePath),
+ [&](sdbusplus::message::message& msg) {
+ userUpdatedSignalHandler(*this, msg);
+ });
+ userMgrRenamedSignal = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::interface(userMgrInterface) +
+ sdbusplus::bus::match::rules::path(userMgrObjBasePath),
+ [&](sdbusplus::message::message& msg) {
+ userUpdatedSignalHandler(*this, msg);
+ });
+ userPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::path_namespace(userObjBasePath) +
+ sdbusplus::bus::match::rules::interface(
+ dBusPropertiesInterface) +
+ sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
+ sdbusplus::bus::match::rules::argN(0, usersInterface),
+ [&](sdbusplus::message::message& msg) {
+ userUpdatedSignalHandler(*this, msg);
+ });
+ signalHndlrObject = true;
+ }
std::map<DbusUserObjPath, DbusUserObjValue> managedObjs;
try
{
@@ -1351,7 +1637,7 @@ void UserAccess::initUserDataFile()
entry("PATH=%s", userMgrObjBasePath));
return;
}
-
+ bool updateRequired = false;
UsersTbl* userData = &usersTbl;
// user index 0 is reserved, starts with 1
for (size_t usrIdx = 1; usrIdx <= ipmiMaxUsers; ++usrIdx)
@@ -1361,7 +1647,6 @@ void UserAccess::initUserDataFile()
{
std::vector<std::string> usrGrps;
std::string usrPriv;
- bool usrEnabled;
std::string userName(
reinterpret_cast<char*>(userData->user[usrIdx].userName), 0,
@@ -1372,12 +1657,15 @@ void UserAccess::initUserDataFile()
auto usrObj = managedObjs.find(usersPath);
if (usrObj != managedObjs.end())
{
+ bool usrEnabled = false;
+
// User exist. Lets check and update other fileds
getUserObjProperties(usrObj->second, usrGrps, usrPriv,
usrEnabled);
if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) ==
usrGrps.end())
{
+ updateRequired = true;
// Group "ipmi" is removed so lets remove user in IPMI
deleteUserIndex(usrIdx);
}
@@ -1393,6 +1681,7 @@ void UserAccess::initUserDataFile()
.userPrivAccess[getUsrMgmtSyncIndex()]
.privilege != priv)
{
+ updateRequired = true;
for (size_t chIndex = 0; chIndex < ipmiMaxChannels;
++chIndex)
{
@@ -1403,6 +1692,7 @@ void UserAccess::initUserDataFile()
}
if (userData->user[usrIdx].userEnabled != usrEnabled)
{
+ updateRequired = true;
userData->user[usrIdx].userEnabled = usrEnabled;
}
}
@@ -1412,6 +1702,7 @@ void UserAccess::initUserDataFile()
}
else
{
+ updateRequired = true;
deleteUserIndex(usrIdx);
}
}
@@ -1423,7 +1714,7 @@ void UserAccess::initUserDataFile()
{
std::vector<std::string> usrGrps;
std::string usrPriv, userName;
- bool usrEnabled;
+ bool usrEnabled = false;
std::string usrObjPath = std::string(usrObj.first);
if (getUserNameFromPath(usrObj.first.str, userName) != 0)
{
@@ -1435,6 +1726,7 @@ void UserAccess::initUserDataFile()
if (std::find(usrGrps.begin(), usrGrps.end(), ipmiGrpName) !=
usrGrps.end())
{
+ updateRequired = true;
// CREATE NEW USER
if (true != addUserEntry(userName, usrPriv, usrEnabled))
{
@@ -1443,8 +1735,11 @@ void UserAccess::initUserDataFile()
}
}
- // All userData slots update done. Lets write the data
- writeUserData();
+ if (updateRequired)
+ {
+ // All userData slots update done. Lets write the data
+ writeUserData();
+ }
return;
}
diff --git a/user_channel/user_mgmt.hpp b/user_channel/user_mgmt.hpp
index 9ea9f6b..159b15c 100644
--- a/user_channel/user_mgmt.hpp
+++ b/user_channel/user_mgmt.hpp
@@ -16,19 +16,19 @@
#pragma once
#include "user_layer.hpp"
-#include <ipmid/api.h>
-
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/interprocess/sync/named_recursive_mutex.hpp>
#include <cstdint>
#include <ctime>
+#include <ipmid/api.hpp>
#include <sdbusplus/bus.hpp>
+#include <variant>
namespace ipmi
{
using DbusUserPropVariant =
- sdbusplus::message::variant<std::vector<std::string>, std::string, bool>;
+ std::variant<std::vector<std::string>, std::string, bool>;
using DbusUserObjPath = sdbusplus::message::object_path;
@@ -74,6 +74,7 @@ struct UserInfo
bool userEnabled;
bool userInSystem;
bool fixedUserName;
+ PayloadAccess payloadAccess[ipmiMaxChannels];
};
/** @struct UsersTbl
@@ -86,6 +87,16 @@ struct UsersTbl
UserInfo user[ipmiMaxUsers + 1];
};
+/** @brief PAM User Authentication check
+ *
+ * @param[in] username - username in string
+ * @param[in] password - password in string
+ *
+ * @return status
+ */
+bool pamUserCheckAuthenticate(std::string_view username,
+ std::string_view password);
+
class UserAccess;
UserAccess& getUserAccessObject();
@@ -155,6 +166,12 @@ class UserAccess
*/
bool isValidUserName(const char* userNameInChar);
+ /** @brief determines whether ipmi is in available groups list
+ *
+ * @return true if ipmi group is present, false otherwise
+ */
+ bool isIpmiInAvailableGroupList();
+
/** @brief provides user id of the user
*
* @param[in] userName - user name
@@ -217,6 +234,16 @@ class UserAccess
*/
ipmi_ret_t setUserPassword(const uint8_t userId, const char* userPassword);
+ /** @brief to set special user password
+ *
+ * @param[in] userName - user name
+ * @param[in] userPassword - new password of the user
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ ipmi_ret_t setSpecialUserPassword(const std::string& userName,
+ const std::string& userPassword);
+
/** @brief to set user privilege and access details
*
* @param[in] userId - user id
@@ -231,6 +258,56 @@ class UserAccess
const UserPrivAccess& privAccess,
const bool& otherPrivUpdates);
+ /** @brief to get user payload access details from userInfo entry.
+ *
+ * @param[in] userInfo - userInfo entry in usersTbl.
+ * @param[out] stdPayload - stdPayloadEnables1 in a 2D-array.
+ * @param[out] oemPayload - oemPayloadEnables1 in a 2D-array.
+ *
+ * @details Update the given 2D-arrays using the payload access details
+ * available in the given userInfo entry (from usersTbl).
+ * This 2D-array will be mapped to a JSON object (which will be written to
+ * a JSON file subsequently).
+ */
+ void readPayloadAccessFromUserInfo(
+ const UserInfo& userInfo,
+ std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+ stdPayload,
+ std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+ oemPayload);
+
+ /** @brief to update user payload access details in userInfo entry.
+ *
+ * @param[in] stdPayload - stdPayloadEnables1 in a 2D-array.
+ * @param[in] oemPayload - oemPayloadEnables1 in a 2D-array.
+ * @param[out] userInfo - userInfo entry in usersTbl.
+ *
+ * @details Update user payload access details of a given userInfo
+ * entry (in usersTbl) with the information provided in given 2D-arrays.
+ * This 2D-array was created out of a JSON object (which was created by
+ * parsing a JSON file).
+ */
+ void updatePayloadAccessInUserInfo(
+ const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+ stdPayload,
+ const std::array<std::array<bool, ipmiMaxChannels>, payloadsPerByte>&
+ oemPayload,
+ UserInfo& userInfo);
+
+ /** @brief to set user payload access details
+ *
+ * @param[in] chNum - channel number
+ * @param[in] operation - Enable / Disable
+ * @param[in] userId - user id
+ * @param[in] payloadAccess - payload access
+ *
+ * @return IPMI_CC_OK for success, others for failure.
+ */
+ ipmi_ret_t setUserPayloadAccess(const uint8_t chNum,
+ const uint8_t operation,
+ const uint8_t userId,
+ const PayloadAccess& payloadAccess);
+
/** @brief reads user management related data from configuration file
*
*/
@@ -321,8 +398,9 @@ class UserAccess
void getSystemPrivAndGroups();
/** @brief function to init user data from configuration & D-Bus objects
+ * and to register for signals
*
*/
- void initUserDataFile();
+ void cacheUserDataFile();
};
} // namespace ipmi
diff --git a/user_channel/usercommands.cpp b/user_channel/usercommands.cpp
index 489eeaf..3396d2d 100644
--- a/user_channel/usercommands.cpp
+++ b/user_channel/usercommands.cpp
@@ -37,6 +37,8 @@ static constexpr uint8_t setPassword = 0x02;
static constexpr uint8_t testPassword = 0x03;
static constexpr uint8_t passwordKeySize20 = 1;
static constexpr uint8_t passwordKeySize16 = 0;
+static constexpr uint8_t enableOperation = 0x00;
+static constexpr uint8_t disableOperation = 0x01;
/** @struct SetUserNameReq
*
@@ -130,7 +132,7 @@ ipmi::RspType<> ipmiSetUserAccess(ipmi::Context::ptr ctx, uint4_t channel,
{
uint8_t sessLimit = sessionLimit.value_or(0);
uint8_t chNum =
- convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx);
+ convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
if (reserved1 != 0 || reserved2 != 0 || sessLimit != 0 ||
(!isValidChannel(chNum)) ||
(!ipmiUserIsValidPrivilege(static_cast<uint8_t>(privilege))) ||
@@ -199,7 +201,7 @@ ipmi::RspType<uint6_t, // max channel users
uint6_t userId, uint2_t reserved2)
{
uint8_t chNum =
- convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx);
+ convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
if (reserved1 != 0 || reserved2 != 0 || (!isValidChannel(chNum)) ||
(EChannelSessSupported::none == getChannelSessionSupport(chNum)))
{
@@ -307,7 +309,7 @@ ipmi_ret_t ipmiGetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
{ // Invalid User ID
log<level::DEBUG>("User Name not found",
- entry("USER-ID:%d", (uint8_t)req->userId));
+ entry("USER-ID=%d", (uint8_t)req->userId));
return IPMI_CC_PARM_OUT_OF_RANGE;
}
GetUserNameResp* resp = static_cast<GetUserNameResp*>(response);
@@ -366,7 +368,7 @@ ipmi_ret_t ipmiSetUserPassword(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
{
log<level::DEBUG>("User Name not found",
- entry("USER-ID:%d", (uint8_t)req->userId));
+ entry("USER-ID=%d", (uint8_t)req->userId));
return IPMI_CC_PARM_OUT_OF_RANGE;
}
if (req->operation == setPassword)
@@ -391,7 +393,7 @@ ipmi_ret_t ipmiSetUserPassword(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
if (password != testPassword)
{
log<level::DEBUG>("Test password failed",
- entry("USER-ID:%d", (uint8_t)req->userId));
+ entry("USER-ID=%d", (uint8_t)req->userId));
return static_cast<ipmi_ret_t>(
IPMISetPasswordReturnCodes::ipmiCCPasswdFailMismatch);
}
@@ -450,7 +452,7 @@ ipmi::RspType<uint8_t, // channel number
{
uint8_t channel =
- convertCurrentChannelNum(static_cast<uint8_t>(chNum), ctx);
+ convertCurrentChannelNum(static_cast<uint8_t>(chNum), ctx->channel);
if (reserved1 || reserved2 || !isValidChannel(channel) ||
!isValidPrivLimit(static_cast<uint8_t>(privLevel)) ||
@@ -485,10 +487,192 @@ ipmi::RspType<uint8_t, // channel number
rmcp, rmcpp, reserved5, oemID, oemAuxillary);
}
+/** @brief implements the set user payload access command.
+ * @param ctx - IPMI context pointer (for channel)
+ * @param channel - channel number (4 bits)
+ * @param reserved1 - skip 4 bits
+ * @param userId - user id (6 bits)
+ * @param operation - access ENABLE /DISABLE. (2 bits)
+ * @param stdPayload0 - IPMI - reserved. (1 bit)
+ * @param stdPayload1 - SOL. (1 bit)
+ * @param stdPayload2 - (1 bit)
+ * @param stdPayload3 - (1 bit)
+ * @param stdPayload4 - (1 bit)
+ * @param stdPayload5 - (1 bit)
+ * @param stdPayload6 - (1 bit)
+ * @param stdPayload7 - (1 bit)
+ * @param stdPayloadEnables2Reserved - (8 bits)
+ * @param oemPayload0 - (1 bit)
+ * @param oemPayload1 - (1 bit)
+ * @param oemPayload2 - (1 bit)
+ * @param oemPayload3 - (1 bit)
+ * @param oemPayload4 - (1 bit)
+ * @param oemPayload5 - (1 bit)
+ * @param oemPayload6 - (1 bit)
+ * @param oemPayload7 - (1 bit)
+ * @param oemPayloadEnables2Reserved - (8 bits)
+ *
+ * @returns IPMI completion code
+ */
+ipmi::RspType<> ipmiSetUserPayloadAccess(
+ ipmi::Context::ptr ctx,
+
+ uint4_t channel, uint4_t reserved,
+
+ uint6_t userId, uint2_t operation,
+
+ bool stdPayload0ipmiReserved, bool stdPayload1SOL, bool stdPayload2,
+ bool stdPayload3, bool stdPayload4, bool stdPayload5, bool stdPayload6,
+ bool stdPayload7,
+
+ uint8_t stdPayloadEnables2Reserved,
+
+ bool oemPayload0, bool oemPayload1, bool oemPayload2, bool oemPayload3,
+ bool oemPayload4, bool oemPayload5, bool oemPayload6, bool oemPayload7,
+
+ uint8_t oemPayloadEnables2Reserved)
+{
+ // Validate the reserved args. Only SOL payload is supported as on date.
+ if (reserved || stdPayload0ipmiReserved || stdPayload2 || stdPayload3 ||
+ stdPayload4 || stdPayload5 || stdPayload6 || stdPayload7 ||
+ oemPayload0 || oemPayload1 || oemPayload2 || oemPayload3 ||
+ oemPayload4 || oemPayload5 || oemPayload6 || oemPayload7 ||
+ stdPayloadEnables2Reserved || oemPayloadEnables2Reserved)
+ {
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ auto chNum =
+ convertCurrentChannelNum(static_cast<uint8_t>(channel), ctx->channel);
+ if ((operation != enableOperation && operation != disableOperation) ||
+ (!isValidChannel(chNum)) ||
+ (getChannelSessionSupport(chNum) == EChannelSessSupported::none))
+ {
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ if (!ipmiUserIsValidUserId(static_cast<uint8_t>(userId)))
+ {
+ return ipmi::responseParmOutOfRange();
+ }
+
+ PayloadAccess payloadAccess = {0};
+ payloadAccess.stdPayloadEnables1[1] = stdPayload1SOL;
+
+ return ipmi::response(ipmiUserSetUserPayloadAccess(
+ chNum, static_cast<uint8_t>(operation), static_cast<uint8_t>(userId),