summaryrefslogtreecommitdiffstats
path: root/apphandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'apphandler.cpp')
-rw-r--r--apphandler.cpp1362
1 files changed, 1000 insertions, 362 deletions
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;
}
OpenPOWER on IntegriCloud