blob: c0e7fc7c0bbcae33e128351757baf8dbe5620170 [file] [log] [blame]
Jaghathiswari Rankappagounder Natarajan2d4836d2018-11-29 14:16:39 -08001/*
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>
Patrick Venture07886992019-03-08 12:53:57 -080023#include <filesystem>
Jaghathiswari Rankappagounder Natarajan2d4836d2018-11-29 14:16:39 -080024#include <fstream>
25#include <regex>
26#include <sstream>
27#include <string>
28#include <system_error>
29#include <unordered_map>
30
31namespace google
32{
33namespace ipmi
34{
Patrick Venture07886992019-03-08 12:53:57 -080035namespace fs = std::filesystem;
Jaghathiswari Rankappagounder Natarajan2d4836d2018-11-29 14:16:39 -080036
37namespace
38{
39
40#ifndef MAX_IPMI_BUFFER
41#define MAX_IPMI_BUFFER 64
42#endif
43
44std::vector<std::tuple<uint32_t, std::string>> pcie_i2c_map;
45
46std::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
78struct PcieSlotCountRequest
79{
80 uint8_t subcommand;
81} __attribute__((packed));
82
83struct PcieSlotCountReply
84{
85 uint8_t subcommand;
86 uint8_t value;
87} __attribute__((packed));
88
89struct PcieSlotI2cBusMappingRequest
90{
91 uint8_t subcommand;
92 uint8_t entry;
93} __attribute__((packed));
94
95struct 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
103ipmi_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
171ipmi_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