google-ipmi-sys: Add support for PCIE slot to I2C bus mapping
Say the bmc has the following information:
root@xyz:/sys/bus/i2c/devices# ls
0-0070 10-0048 4-0049 5-0062 5-0066 5-0074 i2c-1 i2c-15 i2c-18 i2c-20 i2c-4 i2c-7
1-004e 3-0054 4-004d 5-0063 5-0067 9-004c i2c-10 i2c-16 i2c-19 i2c-21 i2c-5 i2c-8
1-004f 4-0048 5-0061 5-0065 5-0072 i2c-0 i2c-14 i2c-17 i2c-2 i2c-3 i2c-6 i2c-9
root@xyz:/sys/bus/i2c/devices# cd i2c-18
root@xyz:/sys/bus/i2c/devices/i2c-18# ls
delete_device i2c-dev mux_device name new_device
of_node power subsystem uevent
root@xyz:/sys/bus/i2c/devices/i2c-18# cd of_node
root@xyz:/sys/bus/i2c/devices/i2c-18/of_node# ls
root@xyz:/sys/bus/i2c/devices/i2c-18/of_node# cat pcie-slot
/pcie-slot/pcie@4root@xyz:/sys/bus/i2c/devices/i2c-18/of_node# cd
root@xyz:~# cd /sys/firmware/devicetree/base
root@xyz:/sys/firmware/devicetree/base# ls
root@xyz:/sys/firmware/devicetree/base# cd pcie-slot/
root@xyz:/sys/firmware/devicetree/base/pcie-slot# ls
name pcie@1a pcie@2a pcie@4 pcie@6
root@xyz:/sys/firmware/devicetree/base/pcie-slot# cd pcie\@4
root@xyz:/sys/firmware/devicetree/base/pcie-slot/pcie@4# ls
name slot-name
root@xyz:/sys/firmware/devicetree/base/pcie-slot/pcie@4# cat slot-name
slot4root@xyz:/sys/firmware/devicetree/base/pcie-slot/pcie@4#
Then the system can send commands to
1) Get the number of pcie slots.
2) Get information about which i2c bus is routed to which pcie slot.
Tested:
Yes;
1) Sending GetPCIeSlotsCount command; Getting pcie slots count as 4.
root@xyz:~# ipmitool -I dbus raw 0x2e 0x32 0x79 0x2b 0x00 0x04
79 2b 00 04 04
3) Sending GetPCIeSlotI2cBusMapping command with entry ID 0; expecting success;
received "slot5".
root@xyz:~# ipmitool -I dbus raw 0x2e 0x32 0x79 0x2b 0x00 0x05 0x00
79 2b 00 05 14 05 73 6c 6f 74 35
4) Sending GetPCIeSlotI2cBusMapping command with entry ID 1; expecting success;
received "slot1".
root@xyz:~# ipmitool -I dbus raw 0x2e 0x32 0x79 0x2b 0x00 0x05 0x01
79 2b 00 05 0e 05 73 6c 6f 74 31
5) Sending GetPCIeSlotI2cBusMapping command with entry ID 2; expecting success;
received "slot3".
root@xyz:~# ipmitool -I dbus raw 0x2e 0x32 0x79 0x2b 0x00 0x05 0x02
79 2b 00 05 12 05 73 6c 6f 74 33
6) Sending GetPCIeSlotI2cBusMapping command with entry ID 3; expecting success;
received "slot2".
root@xyz:~# ipmitool -I dbus raw 0x2e 0x32 0x79 0x2b 0x00 0x05 0x03
79 2b 00 05 10 05 73 6c 6f 74 32
7) Sending GetPCIeSlotI2cBusMapping command with entry ID 4; expecting failure
root@xyz:~# ipmitool -I dbus raw 0x2e 0x32 0x79 0x2b 0x00 0x05 0x04
Unable to send RAW command (channel=0x0 netfn=0x2e lun=0x0 cmd=0x32 rsp=0xc1): Invalid command
root@xyz:~#
Change-Id: Id6e514bb11e8d4a00c615cffea6c2fdb6f5df03e
Signed-off-by: Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>
diff --git a/Makefile.am b/Makefile.am
index 84b0c56..953a5ae 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,7 +7,7 @@
libsyscmdsdir = ${libdir}/ipmid-providers
libsyscmds_LTLIBRARIES = libsyscmds.la
-libsyscmds_la_SOURCES = main.cpp cable.cpp cpld.cpp eth.cpp psu.cpp
+libsyscmds_la_SOURCES = main.cpp cable.cpp cpld.cpp eth.cpp psu.cpp pcie_i2c.cpp
libsyscmds_la_LDFLAGS = \
$(SDBUSPLUS_LIBS) \
diff --git a/README.md b/README.md
index 5486171..f3b7fae 100644
--- a/README.md
+++ b/README.md
@@ -93,3 +93,43 @@
|Byte(s) |Value |Data
|--------|-------|----
|0x00|0x03|Subcommand
+
+### GetPCIeSlotsCount - SubCommand 0x04 {#getpcieslotscount-0x04}
+
+Sys can get the total number of PCIe slots from BMC using this command. When BMC receives this command, BMC can enumerate over all the PCIe slots and create a hashmap with all the available PCIe slot name - I2C bus number mappings. BMC can then send the total number of PCIe slots as part of this command response.
+
+Request
+
+|Byte(s) |Value |Data
+|--------|-------|----
+|0x00|0x04|Subcommand
+
+
+Response
+
+|Byte(s) |Value |Data
+|--------|-------|----
+|0x00|0x04|Subcommand
+|0x01|Total number of PCIe slots|Total number of PCIe slots
+
+
+### GetPCIeSlotI2cBusMapping - SubCommand 0x05 {#getpciesloti2cbusmapping-0x05}
+
+If Sys gets N total slots as part of the above command, then Sys can send this command N times with Entry IDs ranging from 0 to N - 1. Say, Sys sends this command with Entry ID as 1, BMC can go and fetch the first PCIe slot name - I2C bus number mapping from the hashmap created above and then send the PCIe slot name and I2C bus number as part of the command response.
+
+Request
+
+|Byte(s) |Value |Data
+|--------|-------|----
+|0x00|0x05|Subcommand
+|0x01|Entry ID|Entry ID ranging from 0 to N - 1
+
+
+Response
+
+|Byte(s) |Value |Data
+|--------|-------|----
+|0x00|0x05|Subcommand
+|0x01|I2C bus number|The I2C bus number which is input to the above PCIe slot
+|0x02|PCIe slot name length|The PCIe slot name length
+|0x03...|PCIe slot name|The PCIe slot name without null terminator
diff --git a/main.cpp b/main.cpp
index eaa82ab..f0c61a9 100644
--- a/main.cpp
+++ b/main.cpp
@@ -19,6 +19,7 @@
#include "cable.hpp"
#include "cpld.hpp"
#include "eth.hpp"
+#include "pcie_i2c.hpp"
#include "psu.hpp"
#include <host-ipmid/ipmid-api.h>
@@ -62,6 +63,10 @@
return GetEthDevice(reqBuf, replyCmdBuf, dataLen);
case SysPsuHardReset:
return PsuHardReset(reqBuf, replyCmdBuf, dataLen);
+ case SysPcieSlotCount:
+ return PcieSlotCount(reqBuf, replyCmdBuf, dataLen);
+ case SysPcieSlotI2cBusMapping:
+ return PcieSlotI2cBusMapping(reqBuf, replyCmdBuf, dataLen);
default:
std::fprintf(stderr, "Invalid subcommand: 0x%x\n", reqBuf[0]);
return IPMI_CC_INVALID;
diff --git a/main.hpp b/main.hpp
index f34e2a3..f1cfccf 100644
--- a/main.hpp
+++ b/main.hpp
@@ -15,6 +15,10 @@
SysGetEthDevice = 2,
// The Sys psu hard reset command.
SysPsuHardReset = 3,
+ // The Sys pcie slot count command.
+ SysPcieSlotCount = 4,
+ // The Sys pcie slot to i2c bus mapping command.
+ SysPcieSlotI2cBusMapping = 5,
};
} // namespace ipmi
diff --git a/pcie_i2c.cpp b/pcie_i2c.cpp
new file mode 100644
index 0000000..80d545a
--- /dev/null
+++ b/pcie_i2c.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pcie_i2c.hpp"
+
+#include "main.hpp"
+
+#include <cstdint>
+#include <cstring>
+#include <experimental/filesystem>
+#include <fstream>
+#include <regex>
+#include <sstream>
+#include <string>
+#include <system_error>
+#include <unordered_map>
+
+namespace google
+{
+namespace ipmi
+{
+namespace fs = std::experimental::filesystem;
+
+namespace
+{
+
+#ifndef MAX_IPMI_BUFFER
+#define MAX_IPMI_BUFFER 64
+#endif
+
+std::vector<std::tuple<uint32_t, std::string>> pcie_i2c_map;
+
+std::string read_file(const std::string& file_name)
+{
+ std::ifstream ifs(file_name);
+ std::string file_content;
+ if (!ifs.is_open())
+ {
+ std::fprintf(stderr, "Unable to open file %s.\n", file_name.c_str());
+ }
+ else
+ {
+ if (ifs >> file_content)
+ {
+ // If the last character is a null terminator; remove it.
+ if (!file_content.empty())
+ {
+ char const& back = file_content.back();
+ if (back == '\0')
+ file_content.pop_back();
+ }
+ return file_content;
+ }
+ else
+ {
+ std::fprintf(stderr, "Unable to read file %s.\n",
+ file_name.c_str());
+ }
+ }
+ return "";
+}
+
+} // namespace
+
+struct PcieSlotCountRequest
+{
+ uint8_t subcommand;
+} __attribute__((packed));
+
+struct PcieSlotCountReply
+{
+ uint8_t subcommand;
+ uint8_t value;
+} __attribute__((packed));
+
+struct PcieSlotI2cBusMappingRequest
+{
+ uint8_t subcommand;
+ uint8_t entry;
+} __attribute__((packed));
+
+struct PcieSlotI2cBusMappingReply
+{
+ uint8_t subcommand;
+ uint8_t i2c_bus_number;
+ uint8_t pcie_slot_name_len;
+ uint8_t pcie_slot_name[0];
+} __attribute__((packed));
+
+ipmi_ret_t PcieSlotCount(const uint8_t* reqBuf, uint8_t* replyBuf,
+ size_t* dataLen)
+{
+ if ((*dataLen) < sizeof(struct PcieSlotCountRequest))
+ {
+ std::fprintf(stderr, "Invalid command length: %u\n",
+ static_cast<uint32_t>(*dataLen));
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ // If there are already entries in the vector, clear them.
+ if (!pcie_i2c_map.empty())
+ pcie_i2c_map.clear();
+
+ // Build a vector with i2c bus to pcie slot mapping.
+ // Iterate through all the devices under "/sys/bus/i2c/devices".
+ for (auto& i2c_dev : fs::directory_iterator("/sys/bus/i2c/devices"))
+ {
+ std::string i2c_dev_path = i2c_dev.path();
+ std::smatch i2c_dev_string_number;
+ std::regex e("(i2c-)(\\d+)");
+ // Check if the device has "i2c-" in its path.
+ if (std::regex_search(i2c_dev_path, i2c_dev_string_number, e))
+ {
+ // Check if the i2c device has "pcie-slot" file under "of-node" dir.
+ std::string pcie_slot_path = i2c_dev_path + "/of_node/pcie-slot";
+ std::string pcie_slot;
+ // Read the "pcie-slot" name from the "pcie-slot" file.
+ pcie_slot = read_file(pcie_slot_path);
+ if (pcie_slot.empty())
+ {
+ continue;
+ }
+ std::string pcie_slot_name;
+ std::string pcie_slot_full_path;
+ // Append the "pcie-slot" name to dts base.
+ pcie_slot_full_path.append("/proc/device-tree");
+ pcie_slot_full_path.append(pcie_slot);
+ // Read the "label" which contains the pcie slot name.
+ pcie_slot_full_path.append("/label");
+ pcie_slot_name = read_file(pcie_slot_full_path);
+ if (pcie_slot_name.empty())
+ {
+ continue;
+ }
+ // Get the i2c bus number from the i2c device path.
+ uint32_t i2c_bus_number = i2c_dev_string_number[2].matched
+ ? std::stoi(i2c_dev_string_number[2])
+ : 0;
+ // Store the i2c bus number and the pcie slot name in the vector.
+ pcie_i2c_map.push_back(
+ std::make_tuple(i2c_bus_number, pcie_slot_name));
+ }
+ }
+
+ struct PcieSlotCountReply reply;
+ reply.subcommand = SysPcieSlotCount;
+ // Fill the pcie slot count as the number of entries in the vector.
+ reply.value = pcie_i2c_map.size();
+
+ std::memcpy(&replyBuf[0], &reply, sizeof(reply));
+
+ // Return the subcommand and the result.
+ (*dataLen) = sizeof(reply);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t PcieSlotI2cBusMapping(const uint8_t* reqBuf, uint8_t* replyBuf,
+ size_t* dataLen)
+{
+ struct PcieSlotI2cBusMappingRequest request;
+
+ if ((*dataLen) < sizeof(request))
+ {
+ std::fprintf(stderr, "Invalid command length: %u\n",
+ static_cast<uint32_t>(*dataLen));
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ // If there are no entries in the vector return error.
+ if (pcie_i2c_map.empty())
+ {
+ return IPMI_CC_INVALID_RESERVATION_ID;
+ }
+
+ std::memcpy(&request, &reqBuf[0], sizeof(request));
+
+ // The valid entries range from 0 to N - 1, N being the total number of
+ // entries in the vector.
+ if (request.entry >= pcie_i2c_map.size())
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ // Get the i2c bus number and the pcie slot name from the vector.
+ uint32_t i2c_bus_number = std::get<0>(pcie_i2c_map[request.entry]);
+ std::string pcie_slot_name = std::get<1>(pcie_i2c_map[request.entry]);
+
+ int length =
+ sizeof(struct PcieSlotI2cBusMappingReply) + pcie_slot_name.length();
+
+ // TODO (jaghu) : Add a way to dynamically receive the MAX_IPMI_BUFFER
+ // value and change error to IPMI_CC_REQUESTED_TOO_MANY_BYTES.
+ if (length > MAX_IPMI_BUFFER)
+ {
+ std::fprintf(stderr, "Response would overflow response buffer\n");
+ return IPMI_CC_INVALID;
+ }
+
+ auto reply =
+ reinterpret_cast<struct PcieSlotI2cBusMappingReply*>(&replyBuf[0]);
+ reply->subcommand = SysPcieSlotI2cBusMapping;
+ // Copy the i2c bus number and the pcie slot name to the reply struct.
+ reply->i2c_bus_number = i2c_bus_number;
+ reply->pcie_slot_name_len = pcie_slot_name.length();
+ std::memcpy(reply->pcie_slot_name, pcie_slot_name.c_str(),
+ pcie_slot_name.length());
+
+ // Return the subcommand and the result.
+ (*dataLen) = length;
+ return IPMI_CC_OK;
+}
+} // namespace ipmi
+} // namespace google
diff --git a/pcie_i2c.hpp b/pcie_i2c.hpp
new file mode 100644
index 0000000..6150226
--- /dev/null
+++ b/pcie_i2c.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <host-ipmid/ipmid-api.h>
+
+namespace google
+{
+namespace ipmi
+{
+
+// Handle the pcie slot count command.
+// Sys can query the number of pcie slots.
+ipmi_ret_t PcieSlotCount(const uint8_t* reqBuf, uint8_t* replyBuf,
+ size_t* dataLen);
+
+// Handle the pcie slot to i2c bus mapping command.
+// Sys can query which i2c bus is routed to which pcie slot.
+ipmi_ret_t PcieSlotI2cBusMapping(const uint8_t* reqBuf, uint8_t* replyBuf,
+ size_t* dataLen);
+
+} // namespace ipmi
+} // namespace google