1 // Copyright 2021 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "util.hpp" 16 17 #include <nlohmann/json.hpp> 18 #include <phosphor-logging/elog-errors.hpp> 19 #include <stdplus/print.hpp> 20 #include <xyz/openbmc_project/Common/error.hpp> 21 22 #include <cstdint> 23 #include <cstdio> 24 #include <filesystem> 25 #include <fstream> 26 #include <regex> 27 #include <string> 28 #include <tuple> 29 #include <vector> 30 31 namespace google 32 { 33 namespace ipmi 34 { 35 namespace fs = std::filesystem; 36 using namespace phosphor::logging; 37 using InternalFailure = 38 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 39 40 nlohmann::json parseConfig(const std::string& file) 41 { 42 std::ifstream jsonFile(file); 43 if (!jsonFile.is_open()) 44 { 45 log<level::ERR>("Entity association JSON file not found"); 46 elog<InternalFailure>(); 47 } 48 49 auto data = nlohmann::json::parse(jsonFile, nullptr, false); 50 if (data.is_discarded()) 51 { 52 log<level::ERR>("Entity association JSON parser failure"); 53 elog<InternalFailure>(); 54 } 55 56 return data; 57 } 58 59 std::string readPropertyFile(const std::string& fileName) 60 { 61 std::ifstream ifs(fileName); 62 std::string contents; 63 64 if (!ifs.is_open()) 65 { 66 auto msg = std::string("Unable to open file ") + fileName.c_str(); 67 log<level::DEBUG>(msg.c_str()); 68 } 69 else 70 { 71 if (ifs >> contents) 72 { 73 // If the last character is a null terminator; remove it. 74 if (!contents.empty()) 75 { 76 const char& back = contents.back(); 77 if (back == '\0') 78 contents.pop_back(); 79 } 80 81 return contents; 82 } 83 else 84 { 85 stdplus::print(stderr, "Unable to read file {}.\n", fileName); 86 } 87 } 88 89 return ""; 90 } 91 92 std::vector<std::tuple<std::uint32_t, std::string>> buildPcieMap() 93 { 94 std::vector<std::tuple<std::uint32_t, std::string>> pcie_i2c_map; 95 96 // Build a vector with i2c bus to pcie slot mapping. 97 // Iterate through all the devices under "/sys/bus/i2c/devices". 98 for (const auto& i2c_dev : fs::directory_iterator("/sys/bus/i2c/devices")) 99 { 100 std::string i2c_dev_path = i2c_dev.path(); 101 std::smatch i2c_dev_string_number; 102 std::regex e("(i2c-)(\\d+)"); 103 104 // Check if the device has "i2c-" in its path. 105 if (std::regex_search(i2c_dev_path, i2c_dev_string_number, e)) 106 { 107 // Check if the i2c device has "pcie-slot" file under "of-node" dir. 108 std::string pcie_slot_path = i2c_dev_path + "/of_node/pcie-slot"; 109 std::string pcie_slot; 110 111 // Read the "pcie-slot" name from the "pcie-slot" file. 112 pcie_slot = readPropertyFile(pcie_slot_path); 113 if (pcie_slot.empty()) 114 { 115 continue; 116 } 117 std::string pcie_slot_name; 118 std::string pcie_slot_full_path; 119 120 // Append the "pcie-slot" name to dts base. 121 pcie_slot_full_path.append("/proc/device-tree"); 122 pcie_slot_full_path.append(pcie_slot); 123 124 // Read the "label" which contains the pcie slot name. 125 pcie_slot_full_path.append("/label"); 126 pcie_slot_name = readPropertyFile(pcie_slot_full_path); 127 128 if (pcie_slot_name.empty()) 129 { 130 continue; 131 } 132 133 // Get the i2c bus number from the i2c device path. 134 std::uint32_t i2c_bus_number = 135 i2c_dev_string_number[2].matched 136 ? std::stoi(i2c_dev_string_number[2]) 137 : 0; 138 // Store the i2c bus number and the pcie slot name in the vector. 139 pcie_i2c_map.push_back( 140 std::make_tuple(i2c_bus_number, pcie_slot_name)); 141 } 142 } 143 144 return pcie_i2c_map; 145 } 146 147 } // namespace ipmi 148 } // namespace google 149