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