xref: /openbmc/bmcweb/features/redfish/lib/pcie.hpp (revision 8d78b7a9f5fbca338a5867d5dd4fa83890d79bc7)
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 "node.hpp"
20 
21 #include <boost/system/linux_error.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 getPCIeDeviceList(std::shared_ptr<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 class SystemPCIeDeviceCollection : public Node
73 {
74   public:
75     template <typename CrowApp>
76     SystemPCIeDeviceCollection(CrowApp &app) :
77         Node(app, "/redfish/v1/Systems/system/PCIeDevices/")
78     {
79         entityPrivileges = {
80             {boost::beast::http::verb::get, {{"Login"}}},
81             {boost::beast::http::verb::head, {{"Login"}}},
82             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
83             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
84             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
85             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
86     }
87 
88   private:
89     /**
90      * Functions triggers appropriate requests on DBus
91      */
92     void doGet(crow::Response &res, const crow::Request &req,
93                const std::vector<std::string> &params) override
94     {
95         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
96         asyncResp->res.jsonValue = {
97             {"@odata.type", "#PCIeDeviceCollection.PCIeDeviceCollection"},
98             {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices"},
99             {"Name", "PCIe Device Collection"},
100             {"Description", "Collection of PCIe Devices"},
101             {"Members", nlohmann::json::array()},
102             {"Members@odata.count", 0}};
103         getPCIeDeviceList(asyncResp, "Members");
104     }
105 };
106 
107 class SystemPCIeDevice : public Node
108 {
109   public:
110     SystemPCIeDevice(CrowApp &app) :
111         Node(app, "/redfish/v1/Systems/system/PCIeDevices/<str>/",
112              std::string())
113     {
114         entityPrivileges = {
115             {boost::beast::http::verb::get, {{"Login"}}},
116             {boost::beast::http::verb::head, {{"Login"}}},
117             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
118             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
119             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
120             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
121     }
122 
123   private:
124     void doGet(crow::Response &res, const crow::Request &req,
125                const std::vector<std::string> &params) override
126     {
127         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
128         if (params.size() != 1)
129         {
130             messages::internalError(asyncResp->res);
131             return;
132         }
133         const std::string &device = params[0];
134 
135         auto getPCIeDeviceCallback =
136             [asyncResp,
137              device](const boost::system::error_code ec,
138                      boost::container::flat_map<
139                          std::string, sdbusplus::message::variant<std::string>>
140                          &pcieDevProperties) {
141                 if (ec)
142                 {
143                     BMCWEB_LOG_DEBUG
144                         << "failed to get PCIe Device properties ec: "
145                         << ec.value() << ": " << ec.message();
146                     if (ec.value() ==
147                         boost::system::linux_error::bad_request_descriptor)
148                     {
149                         messages::resourceNotFound(asyncResp->res, "PCIeDevice",
150                                                    device);
151                     }
152                     else
153                     {
154                         messages::internalError(asyncResp->res);
155                     }
156                     return;
157                 }
158 
159                 asyncResp->res.jsonValue = {
160                     {"@odata.type", "#PCIeDevice.v1_4_0.PCIeDevice"},
161                     {"@odata.id",
162                      "/redfish/v1/Systems/system/PCIeDevices/" + device},
163                     {"Name", "PCIe Device"},
164                     {"Id", device}};
165 
166                 if (std::string *property = std::get_if<std::string>(
167                         &pcieDevProperties["Manufacturer"]);
168                     property)
169                 {
170                     asyncResp->res.jsonValue["Manufacturer"] = *property;
171                 }
172 
173                 if (std::string *property = std::get_if<std::string>(
174                         &pcieDevProperties["DeviceType"]);
175                     property)
176                 {
177                     asyncResp->res.jsonValue["DeviceType"] = *property;
178                 }
179 
180                 asyncResp->res.jsonValue["PCIeFunctions"] = {
181                     {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
182                                       device + "/PCIeFunctions"}};
183             };
184         std::string escapedPath = std::string(pciePath) + "/" + device;
185         dbus::utility::escapePathForDbus(escapedPath);
186         crow::connections::systemBus->async_method_call(
187             std::move(getPCIeDeviceCallback), pcieService, escapedPath,
188             "org.freedesktop.DBus.Properties", "GetAll", pcieDeviceInterface);
189     }
190 };
191 
192 class SystemPCIeFunctionCollection : public Node
193 {
194   public:
195     template <typename CrowApp>
196     SystemPCIeFunctionCollection(CrowApp &app) :
197         Node(app, "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/",
198              std::string())
199     {
200         entityPrivileges = {
201             {boost::beast::http::verb::get, {{"Login"}}},
202             {boost::beast::http::verb::head, {{"Login"}}},
203             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
204             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
205             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
206             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
207     }
208 
209   private:
210     /**
211      * Functions triggers appropriate requests on DBus
212      */
213     void doGet(crow::Response &res, const crow::Request &req,
214                const std::vector<std::string> &params) override
215     {
216         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
217         if (params.size() != 1)
218         {
219             messages::internalError(asyncResp->res);
220             return;
221         }
222         const std::string &device = params[0];
223         asyncResp->res.jsonValue = {
224             {"@odata.type", "#PCIeFunctionCollection.PCIeFunctionCollection"},
225             {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" + device +
226                               "/PCIeFunctions"},
227             {"Name", "PCIe Function Collection"},
228             {"Description",
229              "Collection of PCIe Functions for PCIe Device " + device}};
230 
231         auto getPCIeDeviceCallback =
232             [asyncResp,
233              device](const boost::system::error_code ec,
234                      boost::container::flat_map<
235                          std::string, sdbusplus::message::variant<std::string>>
236                          &pcieDevProperties) {
237                 if (ec)
238                 {
239                     BMCWEB_LOG_DEBUG
240                         << "failed to get PCIe Device properties ec: "
241                         << ec.value() << ": " << ec.message();
242                     if (ec.value() ==
243                         boost::system::linux_error::bad_request_descriptor)
244                     {
245                         messages::resourceNotFound(asyncResp->res, "PCIeDevice",
246                                                    device);
247                     }
248                     else
249                     {
250                         messages::internalError(asyncResp->res);
251                     }
252                     return;
253                 }
254 
255                 nlohmann::json &pcieFunctionList =
256                     asyncResp->res.jsonValue["Members"];
257                 pcieFunctionList = nlohmann::json::array();
258                 static constexpr const int maxPciFunctionNum = 8;
259                 for (int functionNum = 0; functionNum < maxPciFunctionNum;
260                      functionNum++)
261                 {
262                     // Check if this function exists by looking for a device ID
263                     std::string devIDProperty =
264                         "Function" + std::to_string(functionNum) + "DeviceId";
265                     std::string *property = std::get_if<std::string>(
266                         &pcieDevProperties[devIDProperty]);
267                     if (property && !property->empty())
268                     {
269                         pcieFunctionList.push_back(
270                             {{"@odata.id",
271                               "/redfish/v1/Systems/system/PCIeDevices/" +
272                                   device + "/PCIeFunctions/" +
273                                   std::to_string(functionNum)}});
274                     }
275                 }
276                 asyncResp->res.jsonValue["PCIeFunctions@odata.count"] =
277                     pcieFunctionList.size();
278             };
279         std::string escapedPath = std::string(pciePath) + "/" + device;
280         dbus::utility::escapePathForDbus(escapedPath);
281         crow::connections::systemBus->async_method_call(
282             std::move(getPCIeDeviceCallback), pcieService, escapedPath,
283             "org.freedesktop.DBus.Properties", "GetAll", pcieDeviceInterface);
284     }
285 };
286 
287 class SystemPCIeFunction : public Node
288 {
289   public:
290     SystemPCIeFunction(CrowApp &app) :
291         Node(
292             app,
293             "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/",
294             std::string(), std::string())
295     {
296         entityPrivileges = {
297             {boost::beast::http::verb::get, {{"Login"}}},
298             {boost::beast::http::verb::head, {{"Login"}}},
299             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
300             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
301             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
302             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
303     }
304 
305   private:
306     void doGet(crow::Response &res, const crow::Request &req,
307                const std::vector<std::string> &params) override
308     {
309         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
310         if (params.size() != 2)
311         {
312             messages::internalError(asyncResp->res);
313             return;
314         }
315         const std::string &device = params[0];
316         const std::string &function = params[1];
317 
318         auto getPCIeDeviceCallback = [asyncResp, device, function](
319                                          const boost::system::error_code ec,
320                                          boost::container::flat_map<
321                                              std::string,
322                                              sdbusplus::message::variant<
323                                                  std::string>>
324                                              &pcieDevProperties) {
325             if (ec)
326             {
327                 BMCWEB_LOG_DEBUG
328                     << "failed to get PCIe Device properties ec: " << ec.value()
329                     << ": " << ec.message();
330                 if (ec.value() ==
331                     boost::system::linux_error::bad_request_descriptor)
332                 {
333                     messages::resourceNotFound(asyncResp->res, "PCIeDevice",
334                                                device);
335                 }
336                 else
337                 {
338                     messages::internalError(asyncResp->res);
339                 }
340                 return;
341             }
342 
343             // Check if this function exists by looking for a device ID
344             std::string devIDProperty = "Function" + function + "DeviceId";
345             if (std::string *property =
346                     std::get_if<std::string>(&pcieDevProperties[devIDProperty]);
347                 property && property->empty())
348             {
349                 messages::resourceNotFound(asyncResp->res, "PCIeFunction",
350                                            function);
351                 return;
352             }
353 
354             asyncResp->res.jsonValue = {
355                 {"@odata.type", "#PCIeFunction.v1_2_0.PCIeFunction"},
356                 {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
357                                   device + "/PCIeFunctions/" + function},
358                 {"Name", "PCIe Function"},
359                 {"Id", function},
360                 {"FunctionId", std::stoi(function)},
361                 {"Links",
362                  {{"PCIeDevice",
363                    {{"@odata.id",
364                      "/redfish/v1/Systems/system/PCIeDevices/" + device}}}}}};
365 
366             if (std::string *property = std::get_if<std::string>(
367                     &pcieDevProperties["Function" + function + "DeviceId"]);
368                 property)
369             {
370                 asyncResp->res.jsonValue["DeviceId"] = *property;
371             }
372 
373             if (std::string *property = std::get_if<std::string>(
374                     &pcieDevProperties["Function" + function + "VendorId"]);
375                 property)
376             {
377                 asyncResp->res.jsonValue["VendorId"] = *property;
378             }
379 
380             if (std::string *property = std::get_if<std::string>(
381                     &pcieDevProperties["Function" + function + "FunctionType"]);
382                 property)
383             {
384                 asyncResp->res.jsonValue["FunctionType"] = *property;
385             }
386 
387             if (std::string *property = std::get_if<std::string>(
388                     &pcieDevProperties["Function" + function + "DeviceClass"]);
389                 property)
390             {
391                 asyncResp->res.jsonValue["DeviceClass"] = *property;
392             }
393 
394             if (std::string *property = std::get_if<std::string>(
395                     &pcieDevProperties["Function" + function + "ClassCode"]);
396                 property)
397             {
398                 asyncResp->res.jsonValue["ClassCode"] = *property;
399             }
400 
401             if (std::string *property = std::get_if<std::string>(
402                     &pcieDevProperties["Function" + function + "RevisionId"]);
403                 property)
404             {
405                 asyncResp->res.jsonValue["RevisionId"] = *property;
406             }
407 
408             if (std::string *property = std::get_if<std::string>(
409                     &pcieDevProperties["Function" + function + "SubsystemId"]);
410                 property)
411             {
412                 asyncResp->res.jsonValue["SubsystemId"] = *property;
413             }
414 
415             if (std::string *property = std::get_if<std::string>(
416                     &pcieDevProperties["Function" + function +
417                                        "SubsystemVendorId"]);
418                 property)
419             {
420                 asyncResp->res.jsonValue["SubsystemVendorId"] = *property;
421             }
422         };
423         std::string escapedPath = std::string(pciePath) + "/" + device;
424         dbus::utility::escapePathForDbus(escapedPath);
425         crow::connections::systemBus->async_method_call(
426             std::move(getPCIeDeviceCallback), pcieService, escapedPath,
427             "org.freedesktop.DBus.Properties", "GetAll", pcieDeviceInterface);
428     }
429 };
430 
431 } // namespace redfish
432