xref: /openbmc/google-ipmi-sys/util.cpp (revision a2056e9c)
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 <cstdint>
18 #include <cstdio>
19 #include <filesystem>
20 #include <fstream>
21 #include <nlohmann/json.hpp>
22 #include <phosphor-logging/elog-errors.hpp>
23 #include <regex>
24 #include <string>
25 #include <tuple>
26 #include <vector>
27 #include <xyz/openbmc_project/Common/error.hpp>
28 
29 namespace google
30 {
31 namespace ipmi
32 {
33 namespace fs = std::filesystem;
34 using namespace phosphor::logging;
35 using InternalFailure =
36     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
37 
38 nlohmann::json parseConfig(const std::string& file)
39 {
40     std::ifstream jsonFile(file);
41     if (!jsonFile.is_open())
42     {
43         log<level::ERR>("Entity association JSON file not found");
44         elog<InternalFailure>();
45     }
46 
47     auto data = nlohmann::json::parse(jsonFile, nullptr, false);
48     if (data.is_discarded())
49     {
50         log<level::ERR>("Entity association JSON parser failure");
51         elog<InternalFailure>();
52     }
53 
54     return data;
55 }
56 
57 std::string readPropertyFile(const std::string& fileName)
58 {
59     std::ifstream ifs(fileName);
60     std::string contents;
61 
62     if (!ifs.is_open())
63     {
64         std::fprintf(stderr, "Unable to open file %s.\n", fileName.c_str());
65     }
66     else
67     {
68         if (ifs >> contents)
69         {
70             // If the last character is a null terminator; remove it.
71             if (!contents.empty())
72             {
73                 char const& back = contents.back();
74                 if (back == '\0')
75                     contents.pop_back();
76             }
77 
78             return contents;
79         }
80         else
81         {
82             std::fprintf(stderr, "Unable to read file %s.\n", fileName.c_str());
83         }
84     }
85 
86     return "";
87 }
88 
89 std::vector<std::tuple<std::uint32_t, std::string>> buildPcieMap()
90 {
91     std::vector<std::tuple<std::uint32_t, std::string>> pcie_i2c_map;
92 
93     // Build a vector with i2c bus to pcie slot mapping.
94     // Iterate through all the devices under "/sys/bus/i2c/devices".
95     for (const auto& i2c_dev : fs::directory_iterator("/sys/bus/i2c/devices"))
96     {
97         std::string i2c_dev_path = i2c_dev.path();
98         std::smatch i2c_dev_string_number;
99         std::regex e("(i2c-)(\\d+)");
100 
101         // Check if the device has "i2c-" in its path.
102         if (std::regex_search(i2c_dev_path, i2c_dev_string_number, e))
103         {
104             // Check if the i2c device has "pcie-slot" file under "of-node" dir.
105             std::string pcie_slot_path = i2c_dev_path + "/of_node/pcie-slot";
106             std::string pcie_slot;
107 
108             // Read the "pcie-slot" name from the "pcie-slot" file.
109             pcie_slot = readPropertyFile(pcie_slot_path);
110             if (pcie_slot.empty())
111             {
112                 continue;
113             }
114             std::string pcie_slot_name;
115             std::string pcie_slot_full_path;
116 
117             // Append the "pcie-slot" name to dts base.
118             pcie_slot_full_path.append("/proc/device-tree");
119             pcie_slot_full_path.append(pcie_slot);
120 
121             // Read the "label" which contains the pcie slot name.
122             pcie_slot_full_path.append("/label");
123             pcie_slot_name = readPropertyFile(pcie_slot_full_path);
124 
125             if (pcie_slot_name.empty())
126             {
127                 continue;
128             }
129 
130             // Get the i2c bus number from the i2c device path.
131             std::uint32_t i2c_bus_number =
132                 i2c_dev_string_number[2].matched
133                     ? std::stoi(i2c_dev_string_number[2])
134                     : 0;
135             // Store the i2c bus number and the pcie slot name in the vector.
136             pcie_i2c_map.push_back(
137                 std::make_tuple(i2c_bus_number, pcie_slot_name));
138         }
139     }
140 
141     return pcie_i2c_map;
142 }
143 
144 } // namespace ipmi
145 } // namespace google
146