xref: /openbmc/bmcweb/features/redfish/lib/pcie.hpp (revision 1e45fde46ba5e1e1e058f40e4455f4f01ccb476b)
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 =
167                         sdbusplus::message::variant_ns::get_if<std::string>(
168                             &pcieDevProperties["Manufacturer"]);
169                     property)
170                 {
171                     asyncResp->res.jsonValue["Manufacturer"] = *property;
172                 }
173 
174                 if (std::string *property =
175                         sdbusplus::message::variant_ns::get_if<std::string>(
176                             &pcieDevProperties["DeviceType"]);
177                     property)
178                 {
179                     asyncResp->res.jsonValue["DeviceType"] = *property;
180                 }
181 
182                 asyncResp->res.jsonValue["PCIeFunctions"] = {
183                     {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
184                                       device + "/PCIeFunctions"}};
185             };
186         std::string escapedPath = std::string(pciePath) + "/" + device;
187         dbus::utility::escapePathForDbus(escapedPath);
188         crow::connections::systemBus->async_method_call(
189             std::move(getPCIeDeviceCallback), pcieService, escapedPath,
190             "org.freedesktop.DBus.Properties", "GetAll", pcieDeviceInterface);
191     }
192 };
193 
194 class SystemPCIeFunctionCollection : public Node
195 {
196   public:
197     template <typename CrowApp>
198     SystemPCIeFunctionCollection(CrowApp &app) :
199         Node(app, "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/",
200              std::string())
201     {
202         entityPrivileges = {
203             {boost::beast::http::verb::get, {{"Login"}}},
204             {boost::beast::http::verb::head, {{"Login"}}},
205             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
206             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
207             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
208             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
209     }
210 
211   private:
212     /**
213      * Functions triggers appropriate requests on DBus
214      */
215     void doGet(crow::Response &res, const crow::Request &req,
216                const std::vector<std::string> &params) override
217     {
218         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
219         if (params.size() != 1)
220         {
221             messages::internalError(asyncResp->res);
222             return;
223         }
224         const std::string &device = params[0];
225         asyncResp->res.jsonValue = {
226             {"@odata.type", "#PCIeFunctionCollection.PCIeFunctionCollection"},
227             {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" + device +
228                               "/PCIeFunctions"},
229             {"Name", "PCIe Function Collection"},
230             {"Description",
231              "Collection of PCIe Functions for PCIe Device " + device}};
232 
233         auto getPCIeDeviceCallback =
234             [asyncResp,
235              device](const boost::system::error_code ec,
236                      boost::container::flat_map<
237                          std::string, sdbusplus::message::variant<std::string>>
238                          &pcieDevProperties) {
239                 if (ec)
240                 {
241                     BMCWEB_LOG_DEBUG
242                         << "failed to get PCIe Device properties ec: "
243                         << ec.value() << ": " << ec.message();
244                     if (ec.value() ==
245                         boost::system::linux_error::bad_request_descriptor)
246                     {
247                         messages::resourceNotFound(asyncResp->res, "PCIeDevice",
248                                                    device);
249                     }
250                     else
251                     {
252                         messages::internalError(asyncResp->res);
253                     }
254                     return;
255                 }
256 
257                 nlohmann::json &pcieFunctionList =
258                     asyncResp->res.jsonValue["Members"];
259                 pcieFunctionList = nlohmann::json::array();
260                 static constexpr const int maxPciFunctionNum = 8;
261                 for (int functionNum = 0; functionNum < maxPciFunctionNum;
262                      functionNum++)
263                 {
264                     // Check if this function exists by looking for a device ID
265                     std::string devIDProperty =
266                         "Function" + std::to_string(functionNum) + "DeviceId";
267                     std::string *property =
268                         sdbusplus::message::variant_ns::get_if<std::string>(
269                             &pcieDevProperties[devIDProperty]);
270                     if (property && !property->empty())
271                     {
272                         pcieFunctionList.push_back(
273                             {{"@odata.id",
274                               "/redfish/v1/Systems/system/PCIeDevices/" +
275                                   device + "/PCIeFunctions/" +
276                                   std::to_string(functionNum)}});
277                     }
278                 }
279                 asyncResp->res.jsonValue["PCIeFunctions@odata.count"] =
280                     pcieFunctionList.size();
281             };
282         std::string escapedPath = std::string(pciePath) + "/" + device;
283         dbus::utility::escapePathForDbus(escapedPath);
284         crow::connections::systemBus->async_method_call(
285             std::move(getPCIeDeviceCallback), pcieService, escapedPath,
286             "org.freedesktop.DBus.Properties", "GetAll", pcieDeviceInterface);
287     }
288 };
289 
290 class SystemPCIeFunction : public Node
291 {
292   public:
293     SystemPCIeFunction(CrowApp &app) :
294         Node(
295             app,
296             "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/",
297             std::string(), std::string())
298     {
299         entityPrivileges = {
300             {boost::beast::http::verb::get, {{"Login"}}},
301             {boost::beast::http::verb::head, {{"Login"}}},
302             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
303             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
304             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
305             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
306     }
307 
308   private:
309     void doGet(crow::Response &res, const crow::Request &req,
310                const std::vector<std::string> &params) override
311     {
312         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
313         if (params.size() != 2)
314         {
315             messages::internalError(asyncResp->res);
316             return;
317         }
318         const std::string &device = params[0];
319         const std::string &function = params[1];
320 
321         auto getPCIeDeviceCallback =
322             [asyncResp, device, function](
323                 const boost::system::error_code ec,
324                 boost::container::flat_map<
325                     std::string, sdbusplus::message::variant<std::string>>
326                     &pcieDevProperties) {
327                 if (ec)
328                 {
329                     BMCWEB_LOG_DEBUG
330                         << "failed to get PCIe Device properties ec: "
331                         << ec.value() << ": " << ec.message();
332                     if (ec.value() ==
333                         boost::system::linux_error::bad_request_descriptor)
334                     {
335                         messages::resourceNotFound(asyncResp->res, "PCIeDevice",
336                                                    device);
337                     }
338                     else
339                     {
340                         messages::internalError(asyncResp->res);
341                     }
342                     return;
343                 }
344 
345                 // Check if this function exists by looking for a device ID
346                 std::string devIDProperty = "Function" + function + "DeviceId";
347                 if (std::string *property =
348                         sdbusplus::message::variant_ns::get_if<std::string>(
349                             &pcieDevProperties[devIDProperty]);
350                     property && property->empty())
351                 {
352                     messages::resourceNotFound(asyncResp->res, "PCIeFunction",
353                                                function);
354                     return;
355                 }
356 
357                 asyncResp->res.jsonValue = {
358                     {"@odata.type", "#PCIeFunction.v1_2_0.PCIeFunction"},
359                     {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
360                                       device + "/PCIeFunctions/" + function},
361                     {"Name", "PCIe Function"},
362                     {"Id", function},
363                     {"FunctionId", std::stoi(function)},
364                     {"Links",
365                      {{"PCIeDevice",
366                        {{"@odata.id",
367                          "/redfish/v1/Systems/system/PCIeDevices/" +
368                              device}}}}}};
369 
370                 if (std::string *property =
371                         sdbusplus::message::variant_ns::get_if<std::string>(
372                             &pcieDevProperties["Function" + function +
373                                                "DeviceId"]);
374                     property)
375                 {
376                     asyncResp->res.jsonValue["DeviceId"] = *property;
377                 }
378 
379                 if (std::string *property =
380                         sdbusplus::message::variant_ns::get_if<std::string>(
381                             &pcieDevProperties["Function" + function +
382                                                "VendorId"]);
383                     property)
384                 {
385                     asyncResp->res.jsonValue["VendorId"] = *property;
386                 }
387 
388                 if (std::string *property =
389                         sdbusplus::message::variant_ns::get_if<std::string>(
390                             &pcieDevProperties["Function" + function +
391                                                "FunctionType"]);
392                     property)
393                 {
394                     asyncResp->res.jsonValue["FunctionType"] = *property;
395                 }
396 
397                 if (std::string *property =
398                         sdbusplus::message::variant_ns::get_if<std::string>(
399                             &pcieDevProperties["Function" + function +
400                                                "DeviceClass"]);
401                     property)
402                 {
403                     asyncResp->res.jsonValue["DeviceClass"] = *property;
404                 }
405 
406                 if (std::string *property =
407                         sdbusplus::message::variant_ns::get_if<std::string>(
408                             &pcieDevProperties["Function" + function +
409                                                "ClassCode"]);
410                     property)
411                 {
412                     asyncResp->res.jsonValue["ClassCode"] = *property;
413                 }
414 
415                 if (std::string *property =
416                         sdbusplus::message::variant_ns::get_if<std::string>(
417                             &pcieDevProperties["Function" + function +
418                                                "RevisionId"]);
419                     property)
420                 {
421                     asyncResp->res.jsonValue["RevisionId"] = *property;
422                 }
423 
424                 if (std::string *property =
425                         sdbusplus::message::variant_ns::get_if<std::string>(
426                             &pcieDevProperties["Function" + function +
427                                                "SubsystemId"]);
428                     property)
429                 {
430                     asyncResp->res.jsonValue["SubsystemId"] = *property;
431                 }
432 
433                 if (std::string *property =
434                         sdbusplus::message::variant_ns::get_if<std::string>(
435                             &pcieDevProperties["Function" + function +
436                                                "SubsystemVendorId"]);
437                     property)
438                 {
439                     asyncResp->res.jsonValue["SubsystemVendorId"] = *property;
440                 }
441             };
442         std::string escapedPath = std::string(pciePath) + "/" + device;
443         dbus::utility::escapePathForDbus(escapedPath);
444         crow::connections::systemBus->async_method_call(
445             std::move(getPCIeDeviceCallback), pcieService, escapedPath,
446             "org.freedesktop.DBus.Properties", "GetAll", pcieDeviceInterface);
447     }
448 };
449 
450 } // namespace redfish
451