From cd25f43461b41b74d19cd1f93ce301df9c3bd4f2 Mon Sep 17 00:00:00 2001 From: Yong Li Date: Fri, 21 Sep 2018 09:21:14 +0800 Subject: [PATCH] Implement IPMI Master Write-Read command This command can be used for low-level I2C/SMBus write, read, or write-read accesses to the IPMB or private busses behind a management controller. The command can also be used for providing low-level access to devices that provide an SMBus slave interface. Signed-off-by: Yong Li --- apphandler.cpp | 236 ++++++++++++++++++++++++++++++++++++++++++++++ apphandler.hpp | 1 + host-ipmid-whitelist.conf | 1 + 3 files changed, 238 insertions(+) diff --git a/apphandler.cpp b/apphandler.cpp index 17aff2a..2fe79f6 100644 --- a/apphandler.cpp +++ b/apphandler.cpp @@ -8,6 +8,14 @@ #include "types.hpp" #include "utils.hpp" +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -55,6 +63,8 @@ constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID"; constexpr auto bmc_guid_property = "UUID"; constexpr auto bmc_guid_len = 16; +static constexpr uint8_t maxIPMIWriteReadSize = 144; + static constexpr auto redundancyIntf = "xyz.openbmc_project.Software.RedundancyPriority"; static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version"; @@ -86,6 +96,34 @@ typedef struct uint8_t aux[4]; } __attribute__((packed)) ipmi_device_id_t; +typedef struct +{ + uint8_t busId; + uint8_t slaveAddr; + uint8_t readCount; +} __attribute__((packed)) ipmiI2cRwReq; + +typedef struct +{ + uint8_t busId; + uint8_t slaveAddr; + std::vector data; +} ipmiMasterRwWhitelist; + +static std::vector& getWhiteList() +{ + static std::vector rwWhiteList; + return rwWhiteList; +} + +static constexpr const char* whiteListFilename = + "/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* cmdStr = "command"; + /** * @brief Returns the Version info from primary s/w object * @@ -1089,8 +1127,195 @@ writeResponse: return IPMI_CC_OK; } +static int loadI2CWhiteList() +{ + nlohmann::json data = nullptr; + std::ifstream jsonFile(whiteListFilename); + + if (!jsonFile.good()) + { + log("whitelist file not found!"); + return -1; + } + + try + { + data = nlohmann::json::parse(jsonFile, nullptr, false); + } + catch (nlohmann::json::parse_error& e) + { + log("Corrupted whitelist config file", + entry("MSG: %s", e.what())); + return -1; + } + + try + { + unsigned int i = 0; + nlohmann::json filters = data[filtersStr].get(); + getWhiteList().resize(filters.size()); + + for (const auto& it : filters.items()) + { + nlohmann::json filter = it.value(); + if (filter.is_null()) + { + log("Incorrect filter"); + return -1; + } + + getWhiteList()[i].busId = + std::stoul(filter[busIdStr].get(), nullptr, 16); + + getWhiteList()[i].slaveAddr = std::stoul( + filter[slaveAddrStr].get(), nullptr, 16); + + std::string command = filter[cmdStr].get(); + + log("IPMI I2C whitelist ", entry("INDEX=%d", i), + entry("BUS=%d", getWhiteList()[i].busId), + entry("ADDR=0x%x", getWhiteList()[i].slaveAddr), + entry("LEN=0x%x", command.length()), + entry("COMMAND=[%s]", command.c_str())); + + // convert data string + std::istringstream iss(command); + std::string token; + while (std::getline(iss, token, ' ')) + { + log("IPMI I2C command\n", + entry("TOKEN=%s", token.c_str())); + getWhiteList()[i].data.emplace_back( + std::stoul(token, nullptr, 16)); + } + i++; + } + } + catch (std::exception& e) + { + log("unexpected exception", entry("ERROR=%s", e.what())); + return -1; + } + return 0; +} + +ipmi_ret_t ipmiMasterWriteRead(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) +{ + bool foundInList = false; + int ret = 0; + i2c_rdwr_ioctl_data msgRdwr = {0}; + i2c_msg i2cmsg[2] = {0}; + ipmiI2cRwReq* reqi2c = reinterpret_cast(request); + + if (*data_len <= sizeof(ipmiI2cRwReq)) + { + log("Failed in request", entry("LEN=%d", *data_len)); + *data_len = 0; + return IPMI_CC_REQ_DATA_LEN_INVALID; + } + + if (reqi2c->readCount > maxIPMIWriteReadSize) + { + log("Failed in request", entry("R=%d", reqi2c->readCount)); + *data_len = 0; + return IPMI_CC_PARM_OUT_OF_RANGE; + } + + uint8_t* resptr = reinterpret_cast(response); + uint8_t busId = (reqi2c->busId & 0xFF) >> 1; + // Convert the I2C address from 7-bit format + uint8_t i2cAddr = reqi2c->slaveAddr >> 1; + size_t writeCount = *data_len - sizeof(ipmiI2cRwReq); + + log( + "INPUT: ", entry("LEN=%d", *data_len), entry("ID=0x%x", busId), + entry("ADDR=0x%x", reqi2c->slaveAddr), entry("R=%d", reqi2c->readCount), + entry("W=%d", writeCount)); + + *data_len = 0; + + std::vector inBuf(reqi2c->readCount); + std::vector outBuf(writeCount); + uint8_t* reqptr = reinterpret_cast(request); + + reqptr += sizeof(ipmiI2cRwReq); + std::copy(reqptr, reqptr + writeCount, outBuf.begin()); + + log("checking list ", entry("SIZE=%d", getWhiteList().size())); + // command whitelist checking + for (unsigned int i = 0; i < getWhiteList().size(); i++) + { + // TODO add wildchard/regex support + if ((busId == getWhiteList()[i].busId) && + (i2cAddr == getWhiteList()[i].slaveAddr) && + (outBuf == getWhiteList()[i].data)) + { + log("In whitelist"); + foundInList = true; + break; + } + } + + if (!foundInList) + { + log("Request blocked!", entry("BUS=%d", busId), + entry("ADDR=0x%x", reqi2c->slaveAddr)); + return IPMI_CC_INVALID_FIELD_REQUEST; + } + + log("IPMI Master WriteRead ", entry("BUS=%d", busId), + entry("ADDR=0x%x", reqi2c->slaveAddr), + entry("R=%d", reqi2c->readCount), + entry("W=%d", writeCount)); + + std::string i2cBus = "/dev/i2c-" + std::to_string(busId); + + int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); + if (i2cDev < 0) + { + log("Failed in opening i2c device", + entry("BUS=%s", i2cBus.c_str())); + return IPMI_CC_UNSPECIFIED_ERROR; + } + + // write message + i2cmsg[0].addr = i2cAddr; + i2cmsg[0].flags = 0x00; + i2cmsg[0].len = writeCount; + i2cmsg[0].buf = outBuf.data(); + + // read message + i2cmsg[1].addr = i2cAddr; + i2cmsg[1].flags = I2C_M_RD; + i2cmsg[1].len = reqi2c->readCount; + i2cmsg[1].buf = inBuf.data(); + + msgRdwr.msgs = i2cmsg; + msgRdwr.nmsgs = 2; + + ret = ::ioctl(i2cDev, I2C_RDWR, &msgRdwr); + ::close(i2cDev); + + // TODO add completion code support + if (ret < 0) + { + log("RDWR ioctl error", entry("RET=%d", ret)); + return IPMI_CC_UNSPECIFIED_ERROR; + } + + *data_len = msgRdwr.msgs[1].len; + std::copy(msgRdwr.msgs[1].buf, msgRdwr.msgs[1].buf + msgRdwr.msgs[1].len, + resptr); + + return IPMI_CC_OK; +} + void register_netfn_app_functions() { + int ret = -1; + // ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL, ipmi_app_get_bt_capabilities, PRIVILEGE_USER); @@ -1145,6 +1370,17 @@ void register_netfn_app_functions() ipmi_app_channel_info, PRIVILEGE_USER); #endif + ret = loadI2CWhiteList(); + log("i2c white list is loaded", entry("RET=%d", ret), + entry("SIZE=%d", getWhiteList().size())); + if (ret == 0) + { + log("Register Master RW command"); + // + ipmi_register_callback(NETFUN_APP, IPMI_CMD_MASTER_WRITE_READ, NULL, + ipmiMasterWriteRead, PRIVILEGE_OPERATOR); + } + // ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL, ipmi_app_get_sys_guid, PRIVILEGE_USER); diff --git a/apphandler.hpp b/apphandler.hpp index d4dd8e8..f9e5c59 100644 --- a/apphandler.hpp +++ b/apphandler.hpp @@ -19,6 +19,7 @@ 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_MASTER_WRITE_READ = 0x52, IPMI_CMD_GET_CHAN_CIPHER_SUITES = 0x54, IPMI_CMD_SET_SYSTEM_INFO = 0x58, IPMI_CMD_GET_SYSTEM_INFO = 0x59, diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf index c7eb2d8..22a2a3c 100644 --- a/host-ipmid-whitelist.conf +++ b/host-ipmid-whitelist.conf @@ -25,6 +25,7 @@ 0x06:0x36 //: 0x06:0x37 //: 0x06:0x42 //: +0x06:0x52 //: 0x06:0x54 //: 0x0A:0x10 //: 0x0A:0x11 //: -- 2.7.4