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