intel-ipmi-oem: add slot I2C master RW command
This command is used to perform i2c master read/write on PCIE slots.
Tested:
Reading the 0x4e device on slot 2 of riser 1(i2c bus is 17),
the below two commands have the same result:
ipmitool raw 0x3e 0x52 0 0x11 0x4e 8 0
i2cget -f -y 17 0x4e 0 i 8
Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
Change-Id: I03872296345d180571db2205a9a30c08f28b69b6
diff --git a/include/oemcommands.hpp b/include/oemcommands.hpp
index 6852aa9..7e55038 100644
--- a/include/oemcommands.hpp
+++ b/include/oemcommands.hpp
@@ -30,6 +30,7 @@
cmdDisableBMCSystemReset = 0x42,
cmdGetBMCResetDisables = 0x43,
cmdSendEmbeddedFWUpdStatus = 0x44,
+ cmdSlotI2CMasterWriteRead = 0x52,
cmdSetPowerRestoreDelay = 0x54,
cmdGetPowerRestoreDelay = 0x55,
cmdSetFaultIndication = 0x57,
diff --git a/src/manufacturingcommands.cpp b/src/manufacturingcommands.cpp
index a3fcab1..c7ab5cf 100644
--- a/src/manufacturingcommands.cpp
+++ b/src/manufacturingcommands.cpp
@@ -15,6 +15,7 @@
*/
#include <boost/container/flat_map.hpp>
+#include <filesystem>
#include <fstream>
#include <ipmid/api.hpp>
#include <manufacturingcommands.hpp>
@@ -29,6 +30,10 @@
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::seconds(60)); // 1 minute timeout
+static constexpr uint8_t slotAddressTypeBus = 0;
+static constexpr uint8_t slotAddressTypeUniqueid = 1;
+static constexpr uint8_t slotI2CMaxReadSize = 35;
+
static constexpr const char* callbackMgrService =
"xyz.openbmc_project.CallbackManager";
static constexpr const char* callbackMgrIntf =
@@ -676,6 +681,92 @@
return ipmi::responseSuccess(validData, ethData);
}
+/** @brief implements slot master write read IPMI command which can be used for
+ * low-level I2C/SMBus write, read or write-read access for PCIE slots
+ * @param reserved - skip 6 bit
+ * @param addressType - address type
+ * @param bbSlotNum - baseboard slot number
+ * @param riserSlotNum - riser slot number
+ * @param reserved2 - skip 2 bit
+ * @param slaveAddr - slave address
+ * @param readCount - number of bytes to be read
+ * @param writeData - data to be written
+ *
+ * @returns IPMI completion code plus response data
+ */
+ipmi::RspType<std::vector<uint8_t>>
+ appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
+ uint3_t bbSlotNum, uint3_t riserSlotNum,
+ uint2_t resvered2, uint8_t slaveAddr,
+ uint8_t readCount, std::vector<uint8_t> writeData)
+{
+ const size_t writeCount = writeData.size();
+ std::string i2cBus;
+ if (addressType == slotAddressTypeBus)
+ {
+ std::string path = "/dev/i2c-mux/Riser_" +
+ std::to_string(static_cast<uint8_t>(bbSlotNum)) +
+ "_Mux/Pcie_Slot_" +
+ std::to_string(static_cast<uint8_t>(riserSlotNum));
+
+ if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
+ {
+ i2cBus = std::filesystem::read_symlink(path);
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Master write read command: Cannot get BusID");
+ return ipmi::responseInvalidFieldRequest();
+ }
+ }
+ else if (addressType == slotAddressTypeUniqueid)
+ {
+ i2cBus = "/dev/i2c-" +
+ std::to_string(static_cast<uint8_t>(bbSlotNum) |
+ (static_cast<uint8_t>(riserSlotNum) << 3));
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Master write read command: invalid request");
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ // Allow single byte write as it is offset byte to read the data, rest allow
+ // only in MFG mode.
+ if (writeCount > 1)
+ {
+ if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
+ {
+ return ipmi::responseInsufficientPrivilege();
+ }
+ }
+
+ if (readCount > slotI2CMaxReadSize)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Master write read command: Read count exceeds limit");
+ return ipmi::responseParmOutOfRange();
+ }
+
+ if (!readCount && !writeCount)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Master write read command: Read & write count are 0");
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ std::vector<uint8_t> readBuf(readCount);
+
+ ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
+ if (retI2C != ipmi::ccSuccess)
+ {
+ return ipmi::response(retI2C);
+ }
+
+ return ipmi::responseSuccess(readBuf);
+}
} // namespace ipmi
void register_mtm_commands() __attribute__((constructor));
@@ -709,6 +800,12 @@
IPMINetfnIntelOEMGeneralCmd::cmdGetManufacturingData),
ipmi::Privilege::Admin, ipmi::getManufacturingData);
+ ipmi::registerHandler(
+ ipmi::prioOemBase, netfunIntelAppOEM,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnIntelOEMGeneralCmd::cmdSlotI2CMasterWriteRead),
+ ipmi::Privilege::Admin, ipmi::appSlotI2CMasterWriteRead);
+
ipmi::registerFilter(ipmi::netFnOemOne,
[](ipmi::message::Request::ptr request) {
return ipmi::mfgFilterMessage(request);