xref: /openbmc/bmcweb/redfish-core/lib/pcie.hpp (revision 1214b7e7)
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<std::string,
139                                                 std::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<std::string,
235                                                 std::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                                              std::variant<std::string>>&
323                                              pcieDevProperties) {
324             if (ec)
325             {
326                 BMCWEB_LOG_DEBUG
327                     << "failed to get PCIe Device properties ec: " << ec.value()
328                     << ": " << ec.message();
329                 if (ec.value() ==
330                     boost::system::linux_error::bad_request_descriptor)
331                 {
332                     messages::resourceNotFound(asyncResp->res, "PCIeDevice",
333                                                device);
334                 }
335                 else
336                 {
337                     messages::internalError(asyncResp->res);
338                 }
339                 return;
340             }
341 
342             // Check if this function exists by looking for a device ID
343             std::string devIDProperty = "Function" + function + "DeviceId";
344             if (std::string* property =
345                     std::get_if<std::string>(&pcieDevProperties[devIDProperty]);
346                 property && property->empty())
347             {
348                 messages::resourceNotFound(asyncResp->res, "PCIeFunction",
349                                            function);
350                 return;
351             }
352 
353             asyncResp->res.jsonValue = {
354                 {"@odata.type", "#PCIeFunction.v1_2_0.PCIeFunction"},
355                 {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
356                                   device + "/PCIeFunctions/" + function},
357                 {"Name", "PCIe Function"},
358                 {"Id", function},
359                 {"FunctionId", std::stoi(function)},
360                 {"Links",
361                  {{"PCIeDevice",
362                    {{"@odata.id",
363                      "/redfish/v1/Systems/system/PCIeDevices/" + device}}}}}};
364 
365             if (std::string* property = std::get_if<std::string>(
366                     &pcieDevProperties["Function" + function + "DeviceId"]);
367                 property)
368             {
369                 asyncResp->res.jsonValue["DeviceId"] = *property;
370             }
371 
372             if (std::string* property = std::get_if<std::string>(
373                     &pcieDevProperties["Function" + function + "VendorId"]);
374                 property)
375             {
376                 asyncResp->res.jsonValue["VendorId"] = *property;
377             }
378 
379             if (std::string* property = std::get_if<std::string>(
380                     &pcieDevProperties["Function" + function + "FunctionType"]);
381                 property)
382             {
383                 asyncResp->res.jsonValue["FunctionType"] = *property;
384             }
385 
386             if (std::string* property = std::get_if<std::string>(
387                     &pcieDevProperties["Function" + function + "DeviceClass"]);
388                 property)
389             {
390                 asyncResp->res.jsonValue["DeviceClass"] = *property;
391             }
392 
393             if (std::string* property = std::get_if<std::string>(
394                     &pcieDevProperties["Function" + function + "ClassCode"]);
395                 property)
396             {
397                 asyncResp->res.jsonValue["ClassCode"] = *property;
398             }
399 
400             if (std::string* property = std::get_if<std::string>(
401                     &pcieDevProperties["Function" + function + "RevisionId"]);
402                 property)
403             {
404                 asyncResp->res.jsonValue["RevisionId"] = *property;
405             }
406 
407             if (std::string* property = std::get_if<std::string>(
408                     &pcieDevProperties["Function" + function + "SubsystemId"]);
409                 property)
410             {
411                 asyncResp->res.jsonValue["SubsystemId"] = *property;
412             }
413 
414             if (std::string* property = std::get_if<std::string>(
415                     &pcieDevProperties["Function" + function +
416                                        "SubsystemVendorId"]);
417                 property)
418             {
419                 asyncResp->res.jsonValue["SubsystemVendorId"] = *property;
420             }
421         };
422         std::string escapedPath = std::string(pciePath) + "/" + device;
423         dbus::utility::escapePathForDbus(escapedPath);
424         crow::connections::systemBus->async_method_call(
425             std::move(getPCIeDeviceCallback), pcieService, escapedPath,
426             "org.freedesktop.DBus.Properties", "GetAll", pcieDeviceInterface);
427     }
428 };
429 
430 } // namespace redfish
431