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