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