Support dynamically update BMC slave address
On multi-node platform, the BMC slave address is determined by node ID,
so the BMC slave address needs to be updated after node ID detection.
This patch makes slave-mqueue configurable at run time using 'new_device'
and 'delete-device' sysfs interfaces.
The sample to change BMC slave addr:
(Assume original slave addr is 0x20, but it should be 0x22)
echo "0x1010" > /sys/bus/i2c/devices/i2c-0/delete_device
echo "slave-mqueue 0x1011" > /sys/bus/i2c/devices/i2c-0/new_device
Dependency:
[DTS] remove ipmb0 node to support dynamically update BMC slave address
Tested:
1. The I2C slave message can be received correctly after CMC sends signal
of "updateBmcSlaveAddr" (BNP)
2. Check sys file of slave device exists by command like
"ls -l /sys/bus/i2c/devices/0-1011/slave-mqueue" (BNP)
3. IPMI command injected to bus0 via IPMB header can return correct value (WFP)
Change-Id: Ie55715cf940f19e10d265ae7efec4d4bf55744a2
Signed-off-by: Qiang XU <qiang.xu@linux.intel.com>
diff --git a/ipmbbridged.cpp b/ipmbbridged.cpp
index 2018e28..8104043 100644
--- a/ipmbbridged.cpp
+++ b/ipmbbridged.cpp
@@ -18,6 +18,8 @@
#include "ipmbdefines.hpp"
#include "ipmbutils.hpp"
+#include <boost/algorithm/string/replace.hpp>
+#include <filesystem>
#include <fstream>
#include <nlohmann/json.hpp>
#include <phosphor-logging/log.hpp>
@@ -501,6 +503,41 @@
int IpmbChannel::ipmbChannelInit(const char *ipmbI2cSlave,
const char *ipmbI2cMaster)
{
+ // extract bus id from master path and save
+ std::string ipmbI2cMasterStr(ipmbI2cMaster);
+ auto findHyphen = ipmbI2cMasterStr.find("-");
+ std::string busStr = ipmbI2cMasterStr.substr(findHyphen + 1);
+ try
+ {
+ ipmbBusId = std::stoi(busStr);
+ }
+ catch (std::invalid_argument)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmbChannelInit: invalid bus id in master-path config");
+ return -1;
+ }
+
+ // Check if sysfs has device. If not, enable I2C slave driver by command
+ // echo "slave-mqueue 0x1010" > /sys/bus/i2c/devices/i2c-0/new_device
+ bool hasSysfs = std::filesystem::exists(ipmbI2cSlave);
+ if (!hasSysfs)
+ {
+ std::string deviceFileName =
+ "/sys/bus/i2c/devices/i2c-" + busStr + "/new_device";
+ std::string para = "slave-mqueue 0x1010"; // init with BMC addr 0x20
+ std::fstream deviceFile;
+ deviceFile.open(deviceFileName, std::ios::out);
+ if (!deviceFile.good())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmbChannelInit: error opening deviceFile");
+ return -1;
+ }
+ deviceFile << para;
+ deviceFile.close();
+ }
+
// open fd to i2c slave device
ipmbi2cSlaveFd = open(ipmbI2cSlave, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (ipmbi2cSlaveFd < 0)
@@ -517,6 +554,7 @@
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmbChannelInit: error opening ipmbI2cMaster");
close(ipmbi2cSlaveFd);
+ ipmbi2cSlaveFd = 0;
return -1;
}
@@ -528,6 +566,8 @@
"ipmbChannelInit: error setting ipmbi2cMasterFd slave address");
close(ipmbi2cSlaveFd);
close(ipmbi2cMasterFd);
+ ipmbi2cSlaveFd = 0;
+ ipmbi2cMasterFd = 0;
return -1;
}
@@ -549,6 +589,90 @@
return 0;
}
+int IpmbChannel::ipmbChannelUpdateSlaveAddress(const uint8_t newBmcSlaveAddr)
+{
+ if (ipmbi2cSlaveFd > 0)
+ {
+ i2cSlaveSocket.close();
+ close(ipmbi2cSlaveFd);
+ ipmbi2cSlaveFd = 0;
+ }
+
+ // disable old I2C slave driver by command:
+ // echo "0x1010" > /sys/bus/i2c/devices/i2c-0/delete_device
+ std::string deviceFileName;
+ std::string para;
+ std::fstream deviceFile;
+ deviceFileName = "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) +
+ "/delete_device";
+ para = "0x1010"; // align with removed ipmb0 definition in dts file
+ deviceFile.open(deviceFileName, std::ios::out);
+ if (!deviceFile.good())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmbChannelUpdateSlaveAddress: error opening deviceFile to delete "
+ "sysfs");
+ return -1;
+ }
+ deviceFile << para;
+ deviceFile.close();
+
+ // enable new I2C slave driver by command:
+ // echo "slave-mqueue 0x1012" > /sys/bus/i2c/devices/i2c-0/new_device
+ deviceFileName =
+ "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) + "/new_device";
+ std::ostringstream hex;
+ uint16_t addr = 0x1000 + (newBmcSlaveAddr >> 1);
+ hex << std::hex << static_cast<uint16_t>(addr);
+ const std::string &addressHexStr = hex.str();
+ para = "slave-mqueue 0x" + addressHexStr;
+ deviceFile.open(deviceFileName, std::ios::out);
+ if (!deviceFile.good())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmbChannelUpdateSlaveAddress: error opening deviceFile to create "
+ "sysfs");
+ return -1;
+ }
+ deviceFile << para;
+ deviceFile.close();
+
+ // open fd to i2c slave device
+ std::string ipmbI2cSlaveStr = "/sys/bus/i2c/devices/" +
+ std::to_string(ipmbBusId) + "-" +
+ addressHexStr + "/slave-mqueue";
+ ipmbi2cSlaveFd =
+ open(ipmbI2cSlaveStr.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ if (ipmbi2cSlaveFd < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmbChannelInit: error opening ipmbI2cSlave");
+ return -1;
+ }
+
+ // start to receive i2c data as slave
+ i2cSlaveSocket.assign(boost::asio::ip::tcp::v4(), ipmbi2cSlaveFd);
+ i2cSlaveSocket.async_wait(
+ boost::asio::ip::tcp::socket::wait_error,
+ [this](const boost::system::error_code &ec) {
+ if (ec)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error: processI2cEvent()");
+ return;
+ }
+
+ processI2cEvent();
+ });
+
+ return 0;
+}
+
+uint8_t IpmbChannel::getBusId()
+{
+ return ipmbBusId;
+}
+
uint8_t IpmbChannel::getBmcSlaveAddress()
{
return ipmbBmcSlaveAddress;
@@ -741,6 +865,55 @@
return channel->requestAdd(yield, request);
};
+void addUpdateSlaveAddrHandler()
+{
+ // callback to handle dbus signal of updating slave addr
+ std::function<void(sdbusplus::message::message &)> updateSlaveAddrHandler =
+ [](sdbusplus::message::message &message) {
+ uint8_t reqChannel, busId, slaveAddr;
+
+ // valid source of signal, check whether from multi-node manager
+ std::string pathName = message.get_path();
+ if (pathName != "/xyz/openbmc_project/MultiNode/Status")
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "addUpdateSlaveAddrHandler: invalid obj path");
+ return;
+ }
+
+ message.read(reqChannel, busId, slaveAddr);
+
+ IpmbChannel *channel =
+ getChannel(static_cast<ipmbChannelType>(reqChannel));
+ if (channel == nullptr ||
+ reqChannel != static_cast<uint8_t>(ipmbChannelType::ipmb))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "addUpdateSlaveAddrHandler: invalid channel");
+ return;
+ }
+ if (busId != channel->getBusId())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "addUpdateSlaveAddrHandler: invalid busId");
+ return;
+ }
+ if (channel->getBmcSlaveAddress() == slaveAddr)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "addUpdateSlaveAddrHandler: channel bmc slave addr is "
+ "unchanged, do nothing");
+ return;
+ }
+
+ channel->ipmbChannelUpdateSlaveAddress(slaveAddr);
+ };
+
+ static auto match = std::make_unique<sdbusplus::bus::match::match>(
+ static_cast<sdbusplus::bus::bus &>(*conn),
+ "type='signal',member='updateBmcSlaveAddr',", updateSlaveAddrHandler);
+}
+
/**
* @brief Main
*/
@@ -763,6 +936,8 @@
return -1;
}
+ addUpdateSlaveAddrHandler();
+
io.run();
return 0;
}
diff --git a/ipmbbridged.hpp b/ipmbbridged.hpp
index 4d24790..01b2f44 100644
--- a/ipmbbridged.hpp
+++ b/ipmbbridged.hpp
@@ -257,10 +257,14 @@
int ipmbChannelInit(const char *ipmbI2cSlave, const char *ipmbI2cMaster);
+ int ipmbChannelUpdateSlaveAddress(const uint8_t newBmcSlaveAddr);
+
bool seqNumGet(uint8_t &seq);
ipmbChannelType getChannelType();
+ uint8_t getBusId();
+
uint8_t getBmcSlaveAddress();
uint8_t getRqSlaveAddress();
@@ -285,6 +289,7 @@
uint8_t ipmbBmcSlaveAddress;
uint8_t ipmbRqSlaveAddress;
+ uint8_t ipmbBusId;
ipmbChannelType type;