From 42231615d6a1effbfaa581ca41c0d406174feee8 Mon Sep 17 00:00:00 2001 From: Rajashekar Gade Reddy Date: Mon, 23 Mar 2020 22:19:07 +0530 Subject: [PATCH] Add dbus method SlotIpmbRequest Added dbus method SlotIpmbRequest which enables the applications to communicate with add-in cards. This is submitted in down stream because SlotIpmbRequest uses hold and unhold mux kernel patches which are downstream only patches. Tested: busctl call xyz.openbmc_project.Ipmi.Channel.Ipmb /xyz/openbmc_project/Ipmi/Channel/Ipmb org.openbmc.Ipmb SlotIpmbRequest "yyyyyay" // method call (iyyyyay) 0 7 0 1 0 15 0 0 0 0 2 12 87 1 0 0 0 0 0 0 0 // success busctl call xyz.openbmc_project.Ipmi.Channel.Ipmb /xyz/openbmc_project/Ipmi/Channel/Ipmb org.openbmc.Ipmb SlotIpmbRequest "yyyyyay" // method call (iyyyyay) 4 0 0 0 0 0 // failure busctl call xyz.openbmc_project.Ipmi.Channel.Ipmb /xyz/openbmc_project/Ipmi/Channel/Ipmb org.openbmc.Ipmb SlotIpmbRequest "yyyyyay" // method call (iyyyyay) 4 0 0 0 0 0 // failure //This ipmi command internally calls the dbus method SlotIpmbRequest. ipmitool raw 0x3e 0x51 0 0x01 0xb0 0x6 1 00 00 00 00 00 02 0c 57 01 00 00 00 00 00 00 00 //success Note: Tested for all possible negative test cases and it works fine. Signed-off-by: Rajashekar Gade Reddy --- CMakeLists.txt | 2 +- include/linux/i2c.h | 159 +++++++++++++++++++++++++++++++ ipmb-channels.json | 6 ++ ipmbbridged.cpp | 221 +++++++++++++++++++++++++++++++++++++++++++- ipmbbridged.hpp | 8 +- 5 files changed, 392 insertions(+), 4 deletions(-) create mode 100644 include/linux/i2c.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4acdccf..3484a58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set (CMAKE_CXX_STANDARD_REQUIRED ON) #set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") #set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti") -include_directories (${CMAKE_CURRENT_SOURCE_DIR}) +include_directories (${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include) find_package (Boost REQUIRED) include_directories (${Boost_INCLUDE_DIRS}) add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY) diff --git a/include/linux/i2c.h b/include/linux/i2c.h new file mode 100644 index 0000000..a1db9b1 --- /dev/null +++ b/include/linux/i2c.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the i2c-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-2000 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and + Frodo Looijaard */ + +#ifndef _UAPI_LINUX_I2C_H +#define _UAPI_LINUX_I2C_H + +#include + +/** + * struct i2c_msg - an I2C transaction segment beginning with START + * @addr: Slave address, either seven or ten bits. When this is a ten + * bit address, I2C_M_TEN must be set in @flags and the adapter + * must support I2C_FUNC_10BIT_ADDR. + * @flags: I2C_M_RD is handled by all adapters. No other flags may be + * provided unless the adapter exported the relevant I2C_FUNC_* + * flags through i2c_check_functionality(). + * @len: Number of data bytes in @buf being read from or written to the + * I2C slave address. For read transactions where I2C_M_RECV_LEN + * is set, the caller guarantees that this buffer can hold up to + * 32 bytes in addition to the initial length byte sent by the + * slave (plus, if used, the SMBus PEC); and this value will be + * incremented by the number of block data bytes received. + * @buf: The buffer into which data is read, or from which it's written. + * + * An i2c_msg is the low level representation of one segment of an I2C + * transaction. It is visible to drivers in the @i2c_transfer() procedure, + * to userspace from i2c-dev, and to I2C adapter drivers through the + * @i2c_adapter.@master_xfer() method. + * + * Except when I2C "protocol mangling" is used, all I2C adapters implement + * the standard rules for I2C transactions. Each transaction begins with a + * START. That is followed by the slave address, and a bit encoding read + * versus write. Then follow all the data bytes, possibly including a byte + * with SMBus PEC. The transfer terminates with a NAK, or when all those + * bytes have been transferred and ACKed. If this is the last message in a + * group, it is followed by a STOP. Otherwise it is followed by the next + * @i2c_msg transaction segment, beginning with a (repeated) START. + * + * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then + * passing certain @flags may have changed those standard protocol behaviors. + * Those flags are only for use with broken/nonconforming slaves, and with + * adapters which are known to support the specific mangling options they + * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR). + */ +struct i2c_msg { + __u16 addr; /* slave address */ + __u16 flags; +#define I2C_M_RD 0x0001 /* read data, from slave to master */ + /* I2C_M_RD is guaranteed to be 0x0001! */ +#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ +#define I2C_M_HOLD 0x0100 /* for holding a mux path */ +#define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */ + /* makes only sense in kernelspace */ + /* userspace buffers are copied anyway */ +#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ +#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ +#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ + __u16 len; /* msg length */ + __u8 *buf; /* pointer to msg data */ +}; + +/* To determine what functionality is present */ + +#define I2C_FUNC_I2C 0x00000001 +#define I2C_FUNC_10BIT_ADDR 0x00000002 +#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_IGNORE_NAK etc. */ +#define I2C_FUNC_SMBUS_PEC 0x00000008 +#define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */ +#define I2C_FUNC_SLAVE 0x00000020 +#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */ +#define I2C_FUNC_SMBUS_QUICK 0x00010000 +#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 +#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 +#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 +#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 +#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 +#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 +#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 +#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 +#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 +#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */ +#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */ +#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000 + +#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \ + I2C_FUNC_SMBUS_WRITE_BYTE) +#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \ + I2C_FUNC_SMBUS_WRITE_BYTE_DATA) +#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \ + I2C_FUNC_SMBUS_WRITE_WORD_DATA) +#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) +#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) + +#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | \ + I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_PROC_CALL | \ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \ + I2C_FUNC_SMBUS_I2C_BLOCK | \ + I2C_FUNC_SMBUS_PEC) + +/* + * Data for SMBus Messages + */ +#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ +union i2c_smbus_data { + __u8 byte; + __u16 word; + __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ + /* and one more for user-space compatibility */ +}; + +/* i2c_smbus_xfer read or write markers */ +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 + +/* SMBus transaction types (size parameter in the above functions) + Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ +#define I2C_SMBUS_QUICK 0 +#define I2C_SMBUS_BYTE 1 +#define I2C_SMBUS_BYTE_DATA 2 +#define I2C_SMBUS_WORD_DATA 3 +#define I2C_SMBUS_PROC_CALL 4 +#define I2C_SMBUS_BLOCK_DATA 5 +#define I2C_SMBUS_I2C_BLOCK_BROKEN 6 +#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ +#define I2C_SMBUS_I2C_BLOCK_DATA 8 + +#endif /* _UAPI_LINUX_I2C_H */ diff --git a/ipmb-channels.json b/ipmb-channels.json index 0876db7..ff570c6 100644 --- a/ipmb-channels.json +++ b/ipmb-channels.json @@ -11,6 +11,12 @@ "slave-path": "/dev/ipmb-0", "bmc-addr": 32, "remote-addr": 88 + }, + { + "type": "slot-ipmb", + "slave-path": "/dev/ipmb-6", + "bmc-addr": 18, + "remote-addr": 176 } ] } diff --git a/ipmbbridged.cpp b/ipmbbridged.cpp index 3bf8469..6d1be04 100644 --- a/ipmbbridged.cpp +++ b/ipmbbridged.cpp @@ -18,6 +18,11 @@ #include "ipmbdefines.hpp" #include "ipmbutils.hpp" +#include +#include +#include +#include + #include #include #include @@ -40,7 +45,8 @@ auto conn = std::make_shared(io); static std::list ipmbChannels; static const std::unordered_map ipmbChannelTypeMap = {{"me", ipmbChannelType::me}, - {"ipmb", ipmbChannelType::ipmb}}; + {"ipmb", ipmbChannelType::ipmb}, + {"slot-ipmb", ipmbChannelType::slot_ipmb}}; /** * @brief Ipmb request class methods @@ -555,7 +561,10 @@ int IpmbChannel::ipmbChannelInit(const char *ipmbI2cSlave) { std::string deviceFileName = "/sys/bus/i2c/devices/i2c-" + busStr + "/new_device"; - std::string para = "ipmb-dev 0x1010"; // init with BMC addr 0x20 + std::ostringstream param; + param << "ipmb-dev 0x" << std::hex + << static_cast(0x1000 | (ipmbBmcSlaveAddress >> 1)); + std::string para(param.str()); std::fstream deviceFile; deviceFile.open(deviceFileName, std::ios::out); if (!deviceFile.good()) @@ -711,6 +720,171 @@ void IpmbChannel::addFilter(const uint8_t respNetFn, const uint8_t cmd) } } +class Mux +{ + public: + Mux(const std::string &path) : heldMux(false) + { + fd = open(path.c_str(), O_RDWR | O_NONBLOCK); + } + ~Mux() + { + if (heldMux) + { + if (unholdMux() < 0) + { + phosphor::logging::log( + "Error while unholding the bus"); + } + } + if (!(fd < 0)) + { + close(fd); + } + } + + int transferAndHoldMux(const uint8_t slaveAddr, uint8_t *buffer, + const uint8_t len, uint16_t timeout) + { + if (!isMuxFdOpen()) + { + return -1; + } + struct i2c_msg holdmsg[2] = { + {slaveAddr, 0, len, buffer}, + {0, I2C_M_HOLD, sizeof(timeout), (uint8_t *)&timeout}}; + + struct i2c_rdwr_ioctl_data msgrdwr = {&holdmsg[0], 2}; + + int retVal = ioctl(fd, I2C_RDWR, &msgrdwr); + if (retVal >= 0) + { + heldMux = true; + } + return retVal; + } + + bool isMuxFdOpen() + { + if (fd < 0) + { + phosphor::logging::log( + "Error while opening the mux device file"); + return false; + } + return true; + } + + private: + int unholdMux() + { + if (!isMuxFdOpen()) + { + return -1; + } + uint16_t holdtimeout = 0; // unhold the bus + + struct i2c_msg holdmsg = {0, I2C_M_HOLD, sizeof(holdtimeout), + (uint8_t *)&holdtimeout}; + + struct i2c_rdwr_ioctl_data msgrdwr = {&holdmsg, 1}; + + return ioctl(fd, I2C_RDWR, &msgrdwr); + } + + int fd; + bool heldMux; +}; + +std::tuple> + IpmbChannel::slotRequestAdd(boost::asio::yield_context &yield, + std::shared_ptr request, + const uint8_t pcieSlot) +{ + makeRequestValid(request); + std::filesystem::path p = + "/dev/i2c-mux/PCIE_Mux/Pcie_Slot_" + std::to_string(pcieSlot); + + if (!std::filesystem::exists(p) || !std::filesystem::is_symlink(p)) + { + phosphor::logging::log( + "does not exist or not a symlink ", + phosphor::logging::entry("File:%s", p.c_str())); + return returnStatus(ipmbResponseStatus::error); + } + + Mux mux(p); + if (!mux.isMuxFdOpen()) + { + return returnStatus(ipmbResponseStatus::error); + } + + std::vector buffer(0); + if (request->ipmbToi2cConstruct(buffer) != 0) + { + return returnStatus(ipmbResponseStatus::error); + } + + uint8_t size = buffer.size(); + + const uint8_t slaveAddrIndex = 1; + const uint8_t slotIpmbHeader = 2; + + for (int i = 0; i < ipmbNumberOfTries; i++) + { + boost::system::error_code ec; + int i2cRetryCnt = 0; + do + { + if (mux.transferAndHoldMux( + buffer[slaveAddrIndex] >> 1, buffer.data() + slotIpmbHeader, + size - slotIpmbHeader, ipmbRequestRetryTimeout) >= 0) + { + break; + } + + phosphor::logging::log( + "Error sending slot IPMB command"); + i2cRetryCnt++; + } while (i2cRetryCnt < ipmbI2cNumberOfRetries); + + if (i2cRetryCnt == ipmbI2cNumberOfRetries) + { + std::string msgToLog = + "slotRequestAdd: Sent to I2C failed after retries." + " busId=" + + std::to_string(ipmbBusId) + ", error=" + ec.message(); + phosphor::logging::log( + msgToLog.c_str()); + makeRequestInvalid(*request); + return returnStatus(ipmbResponseStatus::error); + } + + request->timer->expires_after( + std::chrono::milliseconds(ipmbRequestRetryTimeout)); + request->timer->async_wait(yield[ec]); + + if (ec && ec != boost::asio::error::operation_aborted) + { + // unexpected error - invalidate request and return generic error + phosphor::logging::log( + "requestAdd: async_wait error"); + makeRequestInvalid(*request); + return returnStatus(ipmbResponseStatus::error); + } + + if (request->state == ipmbRequestState::matched) + { + // matched response, send it to client application + makeRequestInvalid(*request); + return request->returnMatchedResponse(); + } + } + + makeRequestInvalid(*request); + return returnStatus(ipmbResponseStatus::timeout); +} + std::tuple> IpmbChannel::requestAdd(boost::asio::yield_context &yield, std::shared_ptr request) @@ -848,6 +1022,47 @@ static int initializeChannels() return 0; } +auto slotIpmbHandleRequest = + [](boost::asio::yield_context yield, uint8_t addressType, + uint8_t slotNumber, uint8_t targetSlaveAddr, uint8_t netfn, uint8_t cmd, + std::vector dataReceived) { + uint8_t lun = 0; // No support for lun in slot IPMB + IpmbChannel *channel = + getChannel(static_cast(ipmbChannelType::slot_ipmb)); + if (channel == nullptr) + { + phosphor::logging::log( + "slotIpmbHandleRequest: Slot IPMB channel does not exist"); + return returnStatus(ipmbResponseStatus::error); + } + + // check outstanding request list for valid sequence number + uint8_t seqNum = 0; + bool seqValid = channel->seqNumGet(seqNum); + if (!seqValid) + { + phosphor::logging::log( + "slotIpmbHandleRequest: cannot add more requests to the list"); + return returnStatus(ipmbResponseStatus::busy); + } + + uint8_t bmcSlaveAddress = channel->getBmcSlaveAddress(); + uint8_t rqSlaveAddress = targetSlaveAddr; + + // construct the request to add it to outstanding request list + std::shared_ptr request = std::make_shared( + rqSlaveAddress, netfn, ipmbRsLun, bmcSlaveAddress, seqNum, lun, cmd, + dataReceived); + + if (!request->timer) + { + phosphor::logging::log( + "slotIpmbHandleRequest: timer object does not exist"); + return returnStatus(ipmbResponseStatus::error); + } + return channel->slotRequestAdd(yield, request, slotNumber); + }; + auto ipmbHandleRequest = [](boost::asio::yield_context yield, uint8_t reqChannel, uint8_t netfn, uint8_t lun, uint8_t cmd, std::vector dataReceived) { @@ -994,6 +1209,8 @@ int main(int argc, char *argv[]) server.add_interface(ipmbObj, ipmbDbusIntf); ipmbIface->register_method("sendRequest", std::move(ipmbHandleRequest)); + ipmbIface->register_method("SlotIpmbRequest", + std::move(slotIpmbHandleRequest)); ipmbIface->initialize(); if (initializeChannels() < 0) diff --git a/ipmbbridged.hpp b/ipmbbridged.hpp index 052c193..c79ac63 100644 --- a/ipmbbridged.hpp +++ b/ipmbbridged.hpp @@ -155,7 +155,8 @@ enum class ipmbRequestState enum class ipmbChannelType { ipmb = 0, - me = 1 + me = 1, + slot_ipmb = 2 }; /** @@ -293,6 +294,11 @@ class IpmbChannel void ipmbSendI2cFrame(std::shared_ptr> buffer, size_t retriesAttempted); + std::tuple> + slotRequestAdd(boost::asio::yield_context &yield, + std::shared_ptr requestToSend, + const uint8_t pcieSlot); + std::tuple> requestAdd(boost::asio::yield_context &yield, std::shared_ptr requestToSend); -- 2.17.1