xref: /openbmc/bmcweb/redfish-core/lib/pcie.hpp (revision 432a890c)
1 /*
2 // Copyright (c) 2018 Intel Corporation
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 #pragma once
18 
19 #include <app.hpp>
20 #include <boost/system/linux_error.hpp>
21 
22 namespace redfish
23 {
24 
25 static constexpr char const* pcieService = "xyz.openbmc_project.PCIe";
26 static constexpr char const* pciePath = "/xyz/openbmc_project/PCIe";
27 static constexpr char const* pcieDeviceInterface =
28     "xyz.openbmc_project.PCIe.Device";
29 
30 static inline void
31     getPCIeDeviceList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
32                       const std::string& name)
33 {
34     auto getPCIeMapCallback = [asyncResp, name](
35                                   const boost::system::error_code ec,
36                                   std::vector<std::string>& pcieDevicePaths) {
37         if (ec)
38         {
39             BMCWEB_LOG_DEBUG << "no PCIe device paths found ec: "
40                              << ec.message();
41             // Not an error, system just doesn't have PCIe info
42             return;
43         }
44         nlohmann::json& pcieDeviceList = asyncResp->res.jsonValue[name];
45         pcieDeviceList = nlohmann::json::array();
46         for (const std::string& pcieDevicePath : pcieDevicePaths)
47         {
48             size_t devStart = pcieDevicePath.rfind('/');
49             if (devStart == std::string::npos)
50             {
51                 continue;
52             }
53 
54             std::string devName = pcieDevicePath.substr(devStart + 1);
55             if (devName.empty())
56             {
57                 continue;
58             }
59             pcieDeviceList.push_back(
60                 {{"@odata.id",
61                   "/redfish/v1/Systems/system/PCIeDevices/" + devName}});
62         }
63         asyncResp->res.jsonValue[name + "@odata.count"] = pcieDeviceList.size();
64     };
65     crow::connections::systemBus->async_method_call(
66         std::move(getPCIeMapCallback), "xyz.openbmc_project.ObjectMapper",
67         "/xyz/openbmc_project/object_mapper",
68         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
69         std::string(pciePath) + "/", 1, std::array<std::string, 0>());
70 }
71 
72 inline void requestRoutesSystemPCIeDeviceCollection(App& app)
73 {
74     /**
75      * Functions triggers appropriate requests on DBus
76      */
77     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/PCIeDevices/")
78         .privileges({{"Login"}})
79         .methods(boost::beast::http::verb::get)(
80             [](const crow::Request&,
81                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
82 
83             {
84                 asyncResp->res.jsonValue = {
85                     {"@odata.type",
86                      "#PCIeDeviceCollection.PCIeDeviceCollection"},
87                     {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices"},
88                     {"Name", "PCIe Device Collection"},
89                     {"Description", "Collection of PCIe Devices"},
90                     {"Members", nlohmann::json::array()},
91                     {"Members@odata.count", 0}};
92                 getPCIeDeviceList(asyncResp, "Members");
93             });
94 }
95 
96 inline void requestRoutesSystemPCIeDevice(App& app)
97 {
98     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/PCIeDevices/<str>/")
99         .privileges({{"Login"}})
100         .methods(boost::beast::http::verb::get)(
101             [](const crow::Request&,
102                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
103                const std::string& device)
104 
105             {
106                 auto getPCIeDeviceCallback = [asyncResp, device](
107                                                  const boost::system::error_code
108                                                      ec,
109                                                  boost::container::flat_map<
110                                                      std::string,
111                                                      std::variant<std::string>>&
112                                                      pcieDevProperties) {
113                     if (ec)
114                     {
115                         BMCWEB_LOG_DEBUG
116                             << "failed to get PCIe Device properties ec: "
117                             << ec.value() << ": " << ec.message();
118                         if (ec.value() ==
119                             boost::system::linux_error::bad_request_descriptor)
120                         {
121                             messages::resourceNotFound(asyncResp->res,
122                                                        "PCIeDevice", device);
123                         }
124                         else
125                         {
126                             messages::internalError(asyncResp->res);
127                         }
128                         return;
129                     }
130 
131                     asyncResp->res.jsonValue = {
132                         {"@odata.type", "#PCIeDevice.v1_4_0.PCIeDevice"},
133                         {"@odata.id",
134                          "/redfish/v1/Systems/system/PCIeDevices/" + device},
135                         {"Name", "PCIe Device"},
136                         {"Id", device}};
137 
138                     if (std::string* property = std::get_if<std::string>(
139                             &pcieDevProperties["Manufacturer"]);
140                         property)
141                     {
142                         asyncResp->res.jsonValue["Manufacturer"] = *property;
143                     }
144 
145                     if (std::string* property = std::get_if<std::string>(
146                             &pcieDevProperties["DeviceType"]);
147                         property)
148                     {
149                         asyncResp->res.jsonValue["DeviceType"] = *property;
150                     }
151 
152                     asyncResp->res.jsonValue["PCIeFunctions"] = {
153                         {"@odata.id",
154                          "/redfish/v1/Systems/system/PCIeDevices/" + device +
155                              "/PCIeFunctions"}};
156                 };
157                 std::string escapedPath = std::string(pciePath) + "/" + device;
158                 dbus::utility::escapePathForDbus(escapedPath);
159                 crow::connections::systemBus->async_method_call(
160                     std::move(getPCIeDeviceCallback), pcieService, escapedPath,
161                     "org.freedesktop.DBus.Properties", "GetAll",
162                     pcieDeviceInterface);
163             });
164 }
165 
166 inline void requestRoutesSystemPCIeFunctionCollection(App& app)
167 {
168     /**
169      * Functions triggers appropriate requests on DBus
170      */
171     BMCWEB_ROUTE(app,
172                  "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/")
173         .privileges({{"Login"}})
174         .methods(boost::beast::http::verb::get)(
175             [](const crow::Request&,
176                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
177                const std::string& device)
178 
179             {
180                 asyncResp->res.jsonValue = {
181                     {"@odata.type",
182                      "#PCIeFunctionCollection.PCIeFunctionCollection"},
183                     {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
184                                       device + "/PCIeFunctions"},
185                     {"Name", "PCIe Function Collection"},
186                     {"Description",
187                      "Collection of PCIe Functions for PCIe Device " + device}};
188 
189                 auto getPCIeDeviceCallback = [asyncResp, device](
190                                                  const boost::system::error_code
191                                                      ec,
192                                                  boost::container::flat_map<
193                                                      std::string,
194                                                      std::variant<std::string>>&
195                                                      pcieDevProperties) {
196                     if (ec)
197                     {
198                         BMCWEB_LOG_DEBUG
199                             << "failed to get PCIe Device properties ec: "
200                             << ec.value() << ": " << ec.message();
201                         if (ec.value() ==
202                             boost::system::linux_error::bad_request_descriptor)
203                         {
204                             messages::resourceNotFound(asyncResp->res,
205                                                        "PCIeDevice", device);
206                         }
207                         else
208                         {
209                             messages::internalError(asyncResp->res);
210                         }
211                         return;
212                     }
213 
214                     nlohmann::json& pcieFunctionList =
215                         asyncResp->res.jsonValue["Members"];
216                     pcieFunctionList = nlohmann::json::array();
217                     static constexpr const int maxPciFunctionNum = 8;
218                     for (int functionNum = 0; functionNum < maxPciFunctionNum;
219                          functionNum++)
220                     {
221                         // Check if this function exists by looking for a device
222                         // ID
223                         std::string devIDProperty =
224                             "Function" + std::to_string(functionNum) +
225                             "DeviceId";
226                         std::string* property = std::get_if<std::string>(
227                             &pcieDevProperties[devIDProperty]);
228                         if (property && !property->empty())
229                         {
230                             pcieFunctionList.push_back(
231                                 {{"@odata.id",
232                                   "/redfish/v1/Systems/system/PCIeDevices/" +
233                                       device + "/PCIeFunctions/" +
234                                       std::to_string(functionNum)}});
235                         }
236                     }
237                     asyncResp->res.jsonValue["PCIeFunctions@odata.count"] =
238                         pcieFunctionList.size();
239                 };
240                 std::string escapedPath = std::string(pciePath) + "/" + device;
241                 dbus::utility::escapePathForDbus(escapedPath);
242                 crow::connections::systemBus->async_method_call(
243                     std::move(getPCIeDeviceCallback), pcieService, escapedPath,
244                     "org.freedesktop.DBus.Properties", "GetAll",
245                     pcieDeviceInterface);
246             });
247 }
248 
249 inline void requestRoutesSystemPCIeFunction(App& app)
250 {
251     BMCWEB_ROUTE(
252         app,
253         "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/")
254         .privileges({{"Login"}})
255         .methods(
256             boost::beast::http::verb::get)([](const crow::Request&,
257                                               const std::shared_ptr<
258                                                   bmcweb::AsyncResp>& asyncResp,
259                                               const std::string& device,
260                                               const std::string& function) {
261             auto getPCIeDeviceCallback = [asyncResp, device, function](
262                                              const boost::system::error_code ec,
263                                              boost::container::flat_map<
264                                                  std::string,
265                                                  std::variant<std::string>>&
266                                                  pcieDevProperties) {
267                 if (ec)
268                 {
269                     BMCWEB_LOG_DEBUG
270                         << "failed to get PCIe Device properties ec: "
271                         << ec.value() << ": " << ec.message();
272                     if (ec.value() ==
273                         boost::system::linux_error::bad_request_descriptor)
274                     {
275                         messages::resourceNotFound(asyncResp->res, "PCIeDevice",
276                                                    device);
277                     }
278                     else
279                     {
280                         messages::internalError(asyncResp->res);
281                     }
282                     return;
283                 }
284 
285                 // Check if this function exists by looking for a device ID
286                 std::string devIDProperty = "Function" + function + "DeviceId";
287                 if (std::string* property = std::get_if<std::string>(
288                         &pcieDevProperties[devIDProperty]);
289                     property && property->empty())
290                 {
291                     messages::resourceNotFound(asyncResp->res, "PCIeFunction",
292                                                function);
293                     return;
294                 }
295 
296                 asyncResp->res.jsonValue = {
297                     {"@odata.type", "#PCIeFunction.v1_2_0.PCIeFunction"},
298                     {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
299                                       device + "/PCIeFunctions/" + function},
300                     {"Name", "PCIe Function"},
301                     {"Id", function},
302                     {"FunctionId", std::stoi(function)},
303                     {"Links",
304                      {{"PCIeDevice",
305                        {{"@odata.id",
306                          "/redfish/v1/Systems/system/PCIeDevices/" +
307                              device}}}}}};
308 
309                 if (std::string* property = std::get_if<std::string>(
310                         &pcieDevProperties["Function" + function + "DeviceId"]);
311                     property)
312                 {
313                     asyncResp->res.jsonValue["DeviceId"] = *property;
314                 }
315 
316                 if (std::string* property = std::get_if<std::string>(
317                         &pcieDevProperties["Function" + function + "VendorId"]);
318                     property)
319                 {
320                     asyncResp->res.jsonValue["VendorId"] = *property;
321                 }
322 
323                 if (std::string* property = std::get_if<std::string>(
324                         &pcieDevProperties["Function" + function +
325                                            "FunctionType"]);
326                     property)
327                 {
328                     asyncResp->res.jsonValue["FunctionType"] = *property;
329                 }
330 
331                 if (std::string* property = std::get_if<std::string>(
332                         &pcieDevProperties["Function" + function +
333                                            "DeviceClass"]);
334                     property)
335                 {
336                     asyncResp->res.jsonValue["DeviceClass"] = *property;
337                 }
338 
339                 if (std::string* property = std::get_if<std::string>(
340                         &pcieDevProperties["Function" + function +
341                                            "ClassCode"]);
342                     property)
343                 {
344                     asyncResp->res.jsonValue["ClassCode"] = *property;
345                 }
346 
347                 if (std::string* property = std::get_if<std::string>(
348                         &pcieDevProperties["Function" + function +
349                                            "RevisionId"]);
350                     property)
351                 {
352                     asyncResp->res.jsonValue["RevisionId"] = *property;
353                 }
354 
355                 if (std::string* property = std::get_if<std::string>(
356                         &pcieDevProperties["Function" + function +
357                                            "SubsystemId"]);
358                     property)
359                 {
360                     asyncResp->res.jsonValue["SubsystemId"] = *property;
361                 }
362 
363                 if (std::string* property = std::get_if<std::string>(
364                         &pcieDevProperties["Function" + function +
365                                            "SubsystemVendorId"]);
366                     property)
367                 {
368                     asyncResp->res.jsonValue["SubsystemVendorId"] = *property;
369                 }
370             };
371             std::string escapedPath = std::string(pciePath) + "/" + device;
372             dbus::utility::escapePathForDbus(escapedPath);
373             crow::connections::systemBus->async_method_call(
374                 std::move(getPCIeDeviceCallback), pcieService, escapedPath,
375                 "org.freedesktop.DBus.Properties", "GetAll",
376                 pcieDeviceInterface);
377         });
378 }
379 
380 } // namespace redfish
381