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