1 /* 2 * Copyright 2018 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "pcie_i2c.hpp" 18 19 #include "main.hpp" 20 21 #include <cstdint> 22 #include <cstring> 23 #include <experimental/filesystem> 24 #include <fstream> 25 #include <regex> 26 #include <sstream> 27 #include <string> 28 #include <system_error> 29 #include <unordered_map> 30 31 namespace google 32 { 33 namespace ipmi 34 { 35 namespace fs = std::experimental::filesystem; 36 37 namespace 38 { 39 40 #ifndef MAX_IPMI_BUFFER 41 #define MAX_IPMI_BUFFER 64 42 #endif 43 44 std::vector<std::tuple<uint32_t, std::string>> pcie_i2c_map; 45 46 std::string read_file(const std::string& file_name) 47 { 48 std::ifstream ifs(file_name); 49 std::string file_content; 50 if (!ifs.is_open()) 51 { 52 std::fprintf(stderr, "Unable to open file %s.\n", file_name.c_str()); 53 } 54 else 55 { 56 if (ifs >> file_content) 57 { 58 // If the last character is a null terminator; remove it. 59 if (!file_content.empty()) 60 { 61 char const& back = file_content.back(); 62 if (back == '\0') 63 file_content.pop_back(); 64 } 65 return file_content; 66 } 67 else 68 { 69 std::fprintf(stderr, "Unable to read file %s.\n", 70 file_name.c_str()); 71 } 72 } 73 return ""; 74 } 75 76 } // namespace 77 78 struct PcieSlotCountRequest 79 { 80 uint8_t subcommand; 81 } __attribute__((packed)); 82 83 struct PcieSlotCountReply 84 { 85 uint8_t subcommand; 86 uint8_t value; 87 } __attribute__((packed)); 88 89 struct PcieSlotI2cBusMappingRequest 90 { 91 uint8_t subcommand; 92 uint8_t entry; 93 } __attribute__((packed)); 94 95 struct PcieSlotI2cBusMappingReply 96 { 97 uint8_t subcommand; 98 uint8_t i2c_bus_number; 99 uint8_t pcie_slot_name_len; 100 uint8_t pcie_slot_name[0]; 101 } __attribute__((packed)); 102 103 ipmi_ret_t PcieSlotCount(const uint8_t* reqBuf, uint8_t* replyBuf, 104 size_t* dataLen) 105 { 106 if ((*dataLen) < sizeof(struct PcieSlotCountRequest)) 107 { 108 std::fprintf(stderr, "Invalid command length: %u\n", 109 static_cast<uint32_t>(*dataLen)); 110 return IPMI_CC_REQ_DATA_LEN_INVALID; 111 } 112 113 // If there are already entries in the vector, clear them. 114 if (!pcie_i2c_map.empty()) 115 pcie_i2c_map.clear(); 116 117 // Build a vector with i2c bus to pcie slot mapping. 118 // Iterate through all the devices under "/sys/bus/i2c/devices". 119 for (auto& i2c_dev : fs::directory_iterator("/sys/bus/i2c/devices")) 120 { 121 std::string i2c_dev_path = i2c_dev.path(); 122 std::smatch i2c_dev_string_number; 123 std::regex e("(i2c-)(\\d+)"); 124 // Check if the device has "i2c-" in its path. 125 if (std::regex_search(i2c_dev_path, i2c_dev_string_number, e)) 126 { 127 // Check if the i2c device has "pcie-slot" file under "of-node" dir. 128 std::string pcie_slot_path = i2c_dev_path + "/of_node/pcie-slot"; 129 std::string pcie_slot; 130 // Read the "pcie-slot" name from the "pcie-slot" file. 131 pcie_slot = read_file(pcie_slot_path); 132 if (pcie_slot.empty()) 133 { 134 continue; 135 } 136 std::string pcie_slot_name; 137 std::string pcie_slot_full_path; 138 // Append the "pcie-slot" name to dts base. 139 pcie_slot_full_path.append("/proc/device-tree"); 140 pcie_slot_full_path.append(pcie_slot); 141 // Read the "label" which contains the pcie slot name. 142 pcie_slot_full_path.append("/label"); 143 pcie_slot_name = read_file(pcie_slot_full_path); 144 if (pcie_slot_name.empty()) 145 { 146 continue; 147 } 148 // Get the i2c bus number from the i2c device path. 149 uint32_t i2c_bus_number = i2c_dev_string_number[2].matched 150 ? std::stoi(i2c_dev_string_number[2]) 151 : 0; 152 // Store the i2c bus number and the pcie slot name in the vector. 153 pcie_i2c_map.push_back( 154 std::make_tuple(i2c_bus_number, pcie_slot_name)); 155 } 156 } 157 158 struct PcieSlotCountReply reply; 159 reply.subcommand = SysPcieSlotCount; 160 // Fill the pcie slot count as the number of entries in the vector. 161 reply.value = pcie_i2c_map.size(); 162 163 std::memcpy(&replyBuf[0], &reply, sizeof(reply)); 164 165 // Return the subcommand and the result. 166 (*dataLen) = sizeof(reply); 167 168 return IPMI_CC_OK; 169 } 170 171 ipmi_ret_t PcieSlotI2cBusMapping(const uint8_t* reqBuf, uint8_t* replyBuf, 172 size_t* dataLen) 173 { 174 struct PcieSlotI2cBusMappingRequest request; 175 176 if ((*dataLen) < sizeof(request)) 177 { 178 std::fprintf(stderr, "Invalid command length: %u\n", 179 static_cast<uint32_t>(*dataLen)); 180 return IPMI_CC_REQ_DATA_LEN_INVALID; 181 } 182 183 // If there are no entries in the vector return error. 184 if (pcie_i2c_map.empty()) 185 { 186 return IPMI_CC_INVALID_RESERVATION_ID; 187 } 188 189 std::memcpy(&request, &reqBuf[0], sizeof(request)); 190 191 // The valid entries range from 0 to N - 1, N being the total number of 192 // entries in the vector. 193 if (request.entry >= pcie_i2c_map.size()) 194 { 195 return IPMI_CC_PARM_OUT_OF_RANGE; 196 } 197 198 // Get the i2c bus number and the pcie slot name from the vector. 199 uint32_t i2c_bus_number = std::get<0>(pcie_i2c_map[request.entry]); 200 std::string pcie_slot_name = std::get<1>(pcie_i2c_map[request.entry]); 201 202 int length = 203 sizeof(struct PcieSlotI2cBusMappingReply) + pcie_slot_name.length(); 204 205 // TODO (jaghu) : Add a way to dynamically receive the MAX_IPMI_BUFFER 206 // value and change error to IPMI_CC_REQUESTED_TOO_MANY_BYTES. 207 if (length > MAX_IPMI_BUFFER) 208 { 209 std::fprintf(stderr, "Response would overflow response buffer\n"); 210 return IPMI_CC_INVALID; 211 } 212 213 auto reply = 214 reinterpret_cast<struct PcieSlotI2cBusMappingReply*>(&replyBuf[0]); 215 reply->subcommand = SysPcieSlotI2cBusMapping; 216 // Copy the i2c bus number and the pcie slot name to the reply struct. 217 reply->i2c_bus_number = i2c_bus_number; 218 reply->pcie_slot_name_len = pcie_slot_name.length(); 219 std::memcpy(reply->pcie_slot_name, pcie_slot_name.c_str(), 220 pcie_slot_name.length()); 221 222 // Return the subcommand and the result. 223 (*dataLen) = length; 224 return IPMI_CC_OK; 225 } 226 } // namespace ipmi 227 } // namespace google 228